Kinerja a = 0 dan b = 0 dan ... z = 0 vs a + b + c + d = 0

20

Ini adalah pertanyaan sederhana yang sepertinya tidak bisa saya temukan jawabannya.

Dalam hal kinerja, Jika saya memiliki WHEREklausa seperti a=0 and b=0 and ... z=0, Apakah saya akan mendapatkan kinerja apa pun jika saya mengganti kondisi itu dengan a+b+...+z=0?

Dengan kata lain, apakah ada perolehan kinerja dengan mengganti yang berikut ini

Select * 
From MyTable 
Where A=0 and B=0 and C=0 and D=0...

Dengan

Select * 
From MyTable 
Where A+B+C+D=0...

Saya tahu ini bisa bergantung pada indeks, tetapi untuk tujuan ini, anggap saja tidak ada indeks. Apakah operator aritmatika (+) berkinerja lebih baik daripada Operator Logika "ATAU" atau "DAN"?

Saya mendapat kesan bahwa penambahan berkinerja lebih baik daripada beberapa kondisi dengan AND atau OR.

Hasil tes

Di Meja dengan 4,2 juta baris

Mengembalikan baris Di mana A = 0 B = 0 dan C = 0 -> 351748 Baris

Penambahan (A + B + C = 0) mengambil 5 detik sedangkan kondisi Logika A = 0 dan B = 0 dan C = 0 butuh 11 detik.

Di samping itu

Mengembalikan baris di mana A <> 0 B <> 0 atau C <> 0 -> 3829750 Baris 58 detik

Mengembalikan baris Di mana F65 + F67 + f64 <> 0 -> 3829750 Baris 57 detik

Untuk OR, tampaknya tidak ada perbedaan yang signifikan.

Saya setuju dengan gbn:

Jika A adalah -1 dan B adalah 1, A + B = 0 tetapi A = 0 dan B = 0 salah

dan dengan AMtwo:

ABS (A) + ABS (B) + ABS (C) + ABS (D) ... Bahkan jika Anda hanya mengharapkan nilai positif, jika kolom menerima nilai negatif, Anda harus mengasumsikan bahwa Anda mungkin menemukan satu

Hasilnya sangat mengesankan, seperti yang saya pikirkan, Tampaknya penambahannya jauh lebih cepat daripada operator logis.

A = Float, B = Uang dan C = Float. Permintaan yang digunakan adalah seperti yang ditunjukkan. Dalam kasus saya, semua adalah angka positif. Tidak ada indeks. Masuk akal dalam pikiran saya bahwa penambahan akan lebih cepat daripada kondisi logis!

JohnG
sumber
Apakah ini boolean? Berapa banyak kolom yang Anda bicarakan 4 (dalam contoh), atau 26 (dalam judul)? Itu membuat perbedaan. Apa versi SQL Server? Di mana FLOAT dan UANG berperan? Berapa banyak baris yang kita duga? Pertanyaan ini memiliki banyak faktor.
Evan Carroll
@Evan Carroll Mereka bukan Boolean, mereka bukan nomor yang diindeks (int, float, uang, dll). Terlepas dari Versi SQL (SQL2012 dan lebih tinggi), jumlah baris atau kolom, pertanyaannya adalah untuk mengetahui operator mana yang berkinerja lebih baik - operator logika vs operator aritmatika. Seperti yang Anda lihat, Max Vernon mendemonstrasikan teori dengan contoh-contohnya dengan sempurna.
JohnG

Jawaban:

46

Dalam pertanyaan Anda, Anda merinci beberapa tes yang telah disiapkan di tempat Anda "membuktikan" bahwa opsi tambahan lebih cepat daripada membandingkan kolom diskrit. Saya menduga metodologi pengujian Anda mungkin cacat dalam beberapa cara, seperti @gbn dan @srutzky menyinggung.

Pertama, Anda perlu memastikan Anda tidak menguji SQL Server Management Studio (atau klien apa pun yang Anda gunakan). Misalnya, jika Anda menjalankan SELECT *dari tabel dengan 3 juta baris, Anda sebagian besar menguji kemampuan SSMS untuk menarik baris dari SQL Server dan membuat mereka di layar. Anda jauh lebih baik menggunakan sesuatu SELECT COUNT(1)yang meniadakan kebutuhan untuk menarik jutaan baris di jaringan, dan menampilkannya di layar.

Kedua, Anda harus mengetahui cache data SQL Server. Biasanya, kami menguji kecepatan membaca data dari penyimpanan, dan memproses data itu, dari cold-cache (yaitu buffer SQL Server kosong). Kadang-kadang, masuk akal untuk melakukan semua pengujian Anda dengan cache hangat, tetapi Anda perlu mendekati pengujian Anda secara eksplisit dengan mempertimbangkan hal itu.

Untuk tes cold-cache, Anda harus menjalankan CHECKPOINTdan DBCC DROPCLEANBUFFERSsebelum menjalankan setiap tes.

Untuk tes yang Anda tanyakan dalam pertanyaan Anda, saya membuat test-bed berikut:

IF COALESCE(OBJECT_ID('tempdb..#SomeTest'), 0) <> 0
BEGIN
    DROP TABLE #SomeTest;
END
CREATE TABLE #SomeTest
(
    TestID INT NOT NULL
        PRIMARY KEY 
        IDENTITY(1,1)
    , A INT NOT NULL
    , B FLOAT NOT NULL
    , C MONEY NOT NULL
    , D BIGINT NOT NULL
);

INSERT INTO #SomeTest (A, B, C, D)
SELECT o1.object_id, o2.object_id, o3.object_id, o4.object_id
FROM sys.objects o1
    , sys.objects o2
    , sys.objects o3
    , sys.objects o4;

SELECT COUNT(1) 
FROM #SomeTest;

Ini mengembalikan jumlah 260.144.641 pada mesin saya.

Untuk menguji metode "penambahan", saya menjalankan:

CHECKPOINT 5;
DBCC FREEPROCCACHE;
DBCC DROPCLEANBUFFERS;

SET STATISTICS IO, TIME ON;
GO
SELECT COUNT(1)
FROM #SomeTest st
WHERE (st.A + st.B + st.C + st.D) = 0;
GO
SET STATISTICS IO, TIME OFF;

Tab pesan menunjukkan:

Tabel '#SomeTest'. Pindai hitungan 3, pembacaan logis 1322661, pembacaan fisik 0, pembacaan maju 1313877, pembacaan logis lob 0, pembacaan fisik lob 0, pembacaan pembacaan lob depan 0.

Waktu Eksekusi SQL Server: Waktu CPU = 49047 ms, waktu yang berlalu = 173451 ms.

Untuk tes "kolom diskrit":

CHECKPOINT 5;
DBCC FREEPROCCACHE;
DBCC DROPCLEANBUFFERS;

SET STATISTICS IO, TIME ON;
GO
SELECT COUNT(1)
FROM #SomeTest st
WHERE st.A = 0
    AND st.B = 0
    AND st.C = 0
    AND st.D = 0;
GO

SET STATISTICS IO, TIME OFF;

lagi, dari tab pesan:

Tabel '#SomeTest'. Pindai hitungan 3, pembacaan logis 1322661, pembacaan fisik 0, pembacaan maju 1322661, pembacaan logis lob 0, pembacaan fisik lob 0, pembacaan pemb lob depan 0.

Waktu Eksekusi SQL Server: Waktu CPU = 8938 ms, waktu yang berlalu = 162581 ms.

Dari statistik di atas Anda dapat melihat varian kedua, dengan kolom diskrit dibandingkan dengan 0, waktu yang berlalu sekitar 10 detik lebih pendek, dan waktu CPU sekitar 6 kali lebih sedikit. Durasi lama dalam pengujian saya di atas sebagian besar merupakan hasil dari membaca banyak baris dari disk. Jika Anda menurunkan jumlah baris menjadi 3 juta, Anda melihat rasio tetap hampir sama tetapi waktu yang berlalu turun secara nyata, karena disk I / O memiliki efek yang jauh lebih sedikit.

Dengan metode "Tambahan":

Tabel '#SomeTest'. Pindai hitungan 3, pembacaan logis 15255, pembacaan fisik 0, pembacaan read-forward 0, pembacaan logis lob 0, pembacaan fisik lob 0, pembacaan lob baca-depan 0.

Waktu Eksekusi SQL Server: Waktu CPU = 499 ms, waktu yang berlalu = 256 ms.

Dengan metode "kolom diskrit":

Tabel '#SomeTest'. Pindai hitungan 3, pembacaan logis 15255, pembacaan fisik 0, pembacaan read-forward 0, pembacaan logis lob 0, pembacaan fisik lob 0, pembacaan lob baca-depan 0.

Waktu Eksekusi SQL Server: Waktu CPU = 94 ms, waktu yang berlalu = 53 ms.

Apa yang akan membuat perbedaan yang sangat besar untuk tes ini? Indeks yang sesuai, seperti:

CREATE INDEX IX_SomeTest ON #SomeTest(A, B, C, D);

Metode "penambahan":

Tabel '#SomeTest'. Pindai hitungan 3, bacaan logis 14235, bacaan fisik 0, bacaan baca-depan 0, bacaan logis lob 0, bacaan fisik lob 0, bacaan lob baca-depan 0.

Waktu Eksekusi SQL Server: Waktu CPU = 546 ms, waktu yang berlalu = 314 ms.

Metode "kolom diskrit":

Tabel '#SomeTest'. Pindai hitungan 1, bacaan logis 3, bacaan fisik 0, bacaan baca depan 0, bacaan logis lob 0, bacaan fisik lob 0, bacaan baca lob depan 0.

Waktu Eksekusi SQL Server: Waktu CPU = 0 ms, waktu yang berlalu = 0 ms.

Rencana eksekusi untuk setiap permintaan (dengan indeks di atas di tempat) cukup jelas.

Metode "penambahan", yang harus melakukan pemindaian seluruh indeks:

masukkan deskripsi gambar di sini

dan metode "kolom diskrit", yang dapat mencari ke baris pertama indeks di mana kolom indeks utama A, adalah nol:

masukkan deskripsi gambar di sini

Max Vernon
sumber
24

Katakanlah Anda memiliki indeks pada A, B, C, dan D. Bisa juga difilter.

Ini lebih cenderung menggunakan indeks daripada penambahan.

Where A=0 and B=0 and C=0 and D=0

Dalam berita lain, Jika A adalah -1 dan B adalah 1, A+B=0itu benar tetapi A=0 and B=0salah.

gbn
sumber
7

(Harap perhatikan bahwa jawaban ini diajukan sebelum pengujian apa pun dicatat dalam Pertanyaan: teks Pertanyaan berakhir tepat di atas bagian Hasil pengujian .)

Saya akan menebak bahwa ANDkondisi yang terpisah akan lebih disukai karena pengoptimal akan lebih cenderung melakukan hubungan pendek operasi jika satu dari mereka tidak sama dengan 0, tanpa perlu melakukan perhitungan terlebih dahulu.

Namun, karena ini adalah masalah kinerja, Anda harus terlebih dahulu menyiapkan tes untuk menentukan jawaban pada perangkat keras Anda . Laporkan hasil itu, tunjukkan kode pengujian Anda, dan minta orang lain untuk memeriksanya untuk memastikan itu adalah tes yang baik. Mungkin ada faktor-faktor lain yang patut dipertimbangkan yang tidak Anda pikirkan.

Solomon Rutzky
sumber
3

Beberapa alasan umum, jika Anda tidak memiliki indeks di tangan saya tidak berpikir itu akan menjadi masalah yang mana dari dua solusi yang Anda pilih, keduanya akan berkinerja buruk. Jika Anda di sisi lain memiliki indeks pada satu atau lebih kolom dalam predikat, yang pertama kemungkinan akan berkinerja lebih baik daripada yang kedua, karena yang kedua mungkin tidak akan dapat menggunakan indeks.

Disjunctions (OR) secara umum berkinerja lebih buruk daripada conjunctions (AND), tetapi bahkan jika Anda memiliki kueri dengan disjunctions, saya akan menempatkan uang saya pada yang pertama.

Lennart
sumber
2

Ini pertanyaan sederhana

Tidak, bukan. Pertanyaan (semacam) ini adalah apa yang mengganggu banyak DBA dan pengembang perangkat lunak setiap hari, dan itu semua hanya sepele.

yang sepertinya tidak bisa saya temukan jawabannya.

Ya, tidak akan. Setidaknya bukan jawaban umum. Pertama-tama, itu akan sangat tergantung pada RDBMS mana yang Anda gunakan (OK, Anda menggunakan , tapi tetap saja). Bahkan mungkin berubah ketika Anda beralih dari satu versi RDBMS Anda ke yang berikutnya.

Kemudian, itu dapat bergantung pada jumlah detail kecil lainnya, misalnya bagaimana DB Anda menyimpan data, jika Anda memiliki sub-seleksi / gabungan yang mengacaukan masalah pengoptimal paket, dll. Pengoptimal mungkin memberikan Anda berbagai paket eksekusi tergantung pada berapa banyak baris yang Anda miliki ...

Melakukan tes dunia nyata biasanya satu-satunya cara yang berguna untuk menyelesaikan pertanyaan seperti ini. Juga, setiap keuntungan yang didapat oleh optimisasi "misterius" seperti ini biasanya ditelan sepuluh kali lipat oleh pilihan indeks yang cerdas, jadi saya tidak akan repot-repot menghabiskan terlalu banyak waktu untuk menggunakannya, sebelum penggunaan indeks benar-benar dikesampingkan.

AnoE
sumber
0

Ini mungkin jelas, tetapi jika kolomnya INT, maka a+b+cbisa sama dengan nol bahkan ketika tidak ada satupun yang benar-benar nol. Anda sedang menguji dua hal yang berbeda!

Ross Presser
sumber
Baru sadar @gbn menyebutkan ini dalam jawabannya.
Ross Presser