Dengan SourceTable
memiliki catatan> 15MM dan Bad_Phrase
memiliki catatan > 3K, kueri berikut ini membutuhkan waktu hampir 10 jam untuk berjalan di SQL Server 2005 SP4.
UPDATE [SourceTable]
SET
Bad_Count=
(
SELECT
COUNT(*)
FROM Bad_Phrase
WHERE
[SourceTable].Name like '%'+Bad_Phrase.PHRASE+'%'
)
Dalam bahasa Inggris, kueri ini menghitung jumlah frasa berbeda yang tercantum dalam Bad_Phrase yang merupakan substring bidang Name
dalam SourceTable
dan kemudian menempatkan hasil itu di bidang Bad_Count
.
Saya ingin beberapa saran tentang cara menjalankan kueri ini dengan lebih cepat.
sql-server
sql-server-2005
update
subquery
Matthew Lehrer
sumber
sumber
Jawaban:
Sementara saya setuju dengan komentator lain bahwa ini adalah masalah yang mahal secara komputasi, saya pikir ada banyak ruang untuk perbaikan dengan mengubah-ubah SQL yang Anda gunakan. Sebagai ilustrasi, saya membuat kumpulan data palsu dengan nama 15MM dan frase 3K, menjalankan pendekatan lama, dan menjalankan pendekatan baru.
Skrip lengkap untuk menghasilkan kumpulan data palsu dan mencoba pendekatan baru
TL; DR
Di komputer saya dan kumpulan data palsu ini, pendekatan asli memakan waktu sekitar 4 jam untuk dijalankan. Pendekatan baru yang diusulkan membutuhkan waktu sekitar 10 menit , peningkatan yang cukup besar. Berikut ini ringkasan singkat dari pendekatan yang diusulkan:
Pendekatan asli: analisis algoritmik
Dari rencana
UPDATE
pernyataan asli , kita dapat melihat bahwa jumlah pekerjaan sebanding secara linear dengan jumlah nama (15MM) dan jumlah frasa (3K). Jadi, jika kita gandakan jumlah nama dan frasa sebanyak 10, waktu keseluruhan akan menjadi ~ 100 kali lebih lambat.Permintaan sebenarnya sebanding dengan panjang
name
juga; sementara ini sedikit tersembunyi dalam rencana kueri, ia datang melalui "jumlah eksekusi" untuk mencari ke dalam spool tabel. Dalam rencana aktual, kita dapat melihat bahwa ini terjadi bukan hanya sekali pername
, tetapi sebenarnya sekali per karakter offset dalamname
. Jadi pendekatan ini adalah O (# names
*# phrases
*name length
) dalam kompleksitas run-time.Pendekatan baru: kode
Kode ini juga tersedia dalam pastebin lengkap tetapi saya telah menyalinnya di sini untuk kenyamanan. Pastebin juga memiliki definisi prosedur lengkap, yang mencakup variabel
@minId
dan@maxId
yang Anda lihat di bawah untuk menentukan batas-batas batch saat ini.Pendekatan baru: rencana kueri
Pertama, kami membuat substring mulai dari setiap karakter offset
Kemudian buat indeks berkerumun di substring ini
Sekarang, untuk setiap frasa buruk, kami mencari ke dalam substring ini untuk mengidentifikasi kecocokan apa pun. Kami kemudian menghitung jumlah frasa buruk berbeda yang cocok dengan satu atau lebih substring dari string itu. Ini benar-benar langkah kunci; karena cara kami telah mengindeks substring, kami tidak lagi harus memeriksa produk silang lengkap dari frasa dan nama yang buruk. Langkah ini, yang melakukan perhitungan aktual, hanya menyumbang sekitar 10% dari run-time aktual (sisanya adalah pra-pemrosesan substring).
Terakhir, lakukan pernyataan pembaruan aktual, menggunakan a
LEFT OUTER JOIN
untuk menetapkan hitungan 0 pada nama apa pun yang kami tidak menemukan frasa buruk.Pendekatan baru: analisis algoritmik
Pendekatan baru dapat dibagi menjadi dua fase, pra-pemrosesan dan pencocokan. Mari kita mendefinisikan variabel-variabel berikut:
N
= # namaB
= # frasa burukL
= panjang nama rata-rata, dalam karakterFase pra-pemrosesan adalah
O(N*L * LOG(N*L))
untuk membuatN*L
substring dan kemudian mengurutkannya.Pencocokan yang sebenarnya adalah
O(B * LOG(N*L))
untuk mencari ke dalam substring untuk setiap frase buruk.Dengan cara ini, kami telah membuat algoritme yang tidak skala secara linear dengan jumlah frasa buruk, kinerja kunci terbuka saat kami skala ke frasa 3K dan seterusnya. Dengan kata lain, implementasi asli membutuhkan sekitar 10x selama kita beralih dari 300 frase buruk menjadi 3K frase buruk. Demikian pula akan butuh 10x lagi selama kita beralih dari 3K frase buruk menjadi 30K. Implementasi baru, bagaimanapun, akan meningkatkan sub-linear dan pada kenyataannya membutuhkan waktu kurang dari 2x waktu yang diukur pada frase buruk 3K ketika ditingkatkan hingga 30 ribu frase buruk.
Asumsi / Peringatan
SORT
pada substring independen untuk setiap batch dan mudah masuk dalam memori. Anda dapat memanipulasi ukuran batch sesuai kebutuhan, tetapi tidak bijaksana untuk mencoba semua baris 15MM dalam satu batch.Posting blog terkait
Aaron Bertrand mengeksplorasi jenis solusi ini secara lebih rinci di posting blognya. Salah satu cara untuk mendapatkan indeks mencari% wildcard terkemuka .
sumber
Mari kita selesaikan masalah yang sudah jelas yang dibawakan oleh Aaron Bertrand dalam komentar sejenak:
Fakta bahwa subquery Anda menggunakan kartu liar di kedua sisi secara dramatis memengaruhi daya beli . Untuk mengambil kutipan dari posting blog itu:
Ganti kata "nut" untuk setiap "bad word" dan "Product" untuk
SourceTable
, lalu gabungkan dengan komentar Aaron dan Anda harus mulai melihat mengapa sangat sulit (baca tidak mungkin) untuk membuatnya berjalan cepat menggunakan algoritma Anda saat ini.Saya melihat beberapa opsi:
Tergantung pada kebutuhan Anda, saya akan merekomendasikan opsi 3 atau 4.
sumber
pertama itu hanya pembaruan aneh
Seperti '%' + [Bad_Phrase]. [PHRASE] membunuh Anda.
Itu tidak bisa menggunakan indeks
Desain data tidak optimal untuk kecepatan
Bisakah Anda membagi [Bad_Phrase]. [PHRASE] menjadi satu frase / kata?
Jika frasa / kata yang sama muncul lebih dari satu, Anda dapat memasukkannya lebih dari satu kali jika Anda menginginkannya memiliki jumlah yang lebih tinggi.
Jadi, jumlah baris dalam frase buruk akan naik.
Jika Anda dapat, maka ini akan jauh lebih cepat.
Tidak yakin apakah 2005 mendukungnya tetapi Indeks Teks Lengkap dan menggunakan Berisi
sumber