SQL IN () versus OR

23

Saya bekerja dengan kueri yang saya tulis hari ini harus mengubah kode dari WHEREklausa untuk menggunakan filter IN (daftar barang) alih-alih menggunakan sesuatu seperti

item_desc = 'item 1'
OR item_desc = 'item 2'
OR item_desc = 'item 3'
OR item_desc = 'item 4'

Di atas berjalan selama 15 menit dan tidak menghasilkan apa-apa, namun berikut ini memberi saya hasil saya ditetapkan dalam 1,5 menit

item_desc IN (
'item 1'
,'item 2'
,'item 3'
,'item 4'
)

Saya melakukan ini dalam SQL dan saya bertanya-tanya mengapa IN (daftar item) dilakukan jauh lebih cepat daripada pernyataan ATAU.

- EDIT - SQL Server 2008, saya minta maaf karena tidak menaruh sedikit info ini di tempat pertama.

Inilah Query secara keseluruhan menggunakan ORpernyataan:

DECLARE @SD DATETIME
DECLARE @ED DATETIME
SET @SD = '2013-06-01';
SET @ED = '2013-06-15';

-- COLUMN SELECTION
SELECT PV.PtNo_Num AS 'VISIT ID'
, PV.Med_Rec_No AS 'MRN'
, PV.vst_start_dtime AS 'ADMIT'
, PV.vst_end_dtime AS 'DISC'
, PV.Days_Stay AS 'LOS'
, PV.pt_type AS 'PT TYPE'
, PV.hosp_svc AS 'HOSP SVC'
, SO.ord_no AS 'ORDER NUMBER'
--, SO.ent_dtime AS 'ORDER ENTRY TIME'
--, DATEDIFF(HOUR,PV.vst_start_dtime,SO.ent_dtime) AS 'ADM TO ENTRY HOURS'
, SO.svc_desc AS 'ORDER DESCRIPTION'
, OSM.ord_sts AS 'ORDER STATUS'
, SOS.prcs_dtime AS 'ORDER STATUS TIME'
, DATEDIFF(DAY,PV.vst_start_dtime,SOS.prcs_dtime) AS 'ADM TO ORD STS IN DAYS'

-- DB(S) USED
FROM smsdss.BMH_PLM_PtAcct_V PV
JOIN smsmir.sr_ord SO
ON PV.PtNo_Num = SO.episode_no
JOIN smsmir.sr_ord_sts_hist SOS
ON SO.ord_no = SOS.ord_no
JOIN smsmir.ord_sts_modf_mstr OSM
ON SOS.hist_sts = OSM.ord_sts_modf_cd

-- FILTER(S)
WHERE PV.Adm_Date BETWEEN @SD AND @ED
AND SO.svc_cd = 'PCO_REMFOLEY'
OR SO.svc_cd = 'PCO_INSRTFOLEY'
OR SO.svc_cd = 'PCO_INSTFOLEY'
OR SO.svc_cd = 'PCO_URIMETER'

AND SO.ord_no NOT IN (
    SELECT SO.ord_no
    FRROM smsdss.BMH_PLM_PtAcct_V PV
    JOIN smsmir.sr_ord SO
    ON PV.PtNo_Num = SO.episode_no
    JOIN smsmir.sr_ord_sts_hist SOS
    ON SO.ord_no = SOS.ord_no
    JOIN smsmir.ord_sts_modf_mstr OSM
    ON SOS.hist_sts = OSM.ord_sts_modf_cd
    WHERE OSM.ord_sts = 'DISCONTINUE'
    AND SO.svc_cd = 'PCO_REMFOLEY'
    OR SO.svc_cd = 'PCO_INSRTFOLEY'
    OR SO.svc_cd = 'PCO_INSTFOLEY'
    OR SO.svc_cd = 'PCO_URIMETER'
)
ORDER BY PV.PtNo_Num, SO.ord_no, SOS.prcs_dtime

Terima kasih,

MCP_infiltrator
sumber
10
Sudahkah Anda melihat rencana permintaan?
1
Ini adalah implementasi SANGAT spesifik. DBMS mana yang Anda gunakan?
James Anderson
Saya tidak melihat rencana permintaan, saya tidak tahu apakah ini permintaan khusus atau apakah itu masalah fakta, karena dalam hal ini akan selalu bekerja dengan cara ini.
MCP_infiltrator
3
@MCP_infiltrator Jadi rencana eksekusi tidak akan sama karena logikanya tidak setara. Saat menggunakan ORseperti yang Anda lakukan dalam permintaan aktual di atas, Anda mengizinkan engine mengalami hubungan pendek. WHERE A AND B OR Cakan mengevaluasi ke true bahkan jika A DAN B salah, jika C benar. Jika Anda mengatakan WHERE A and B OR C OR D OR E OR Fseperti yang Anda lakukan di atas, AND dapat diperhitungkan. Setara dengan logika yang sebenarnya akan merangkum ORseri di atas dalam kurung sehingga mereka diperlakukan sebagai satu set: WHERE A AND (B OR C OR D OR E). Beginilah cara seorang INdiperlakukan.
JNK
5
Prioritas operator dalam SQL Server ditentukan yang ANDditangani sebelumnya OR, sehingga kueri Anda di atas setara dengan WHERE (OSM.ord_sts = 'DISCONTINUE' AND SO.svc_cd = 'PCO_REMFOLEY') OR SO.svc_cd = 'PCO_INSRTFOLEY' OR SO.svc_cd = 'PCO_INSTFOLEY' OR SO.svc_cd = 'PCO_URIMETER'yang berarti jika salah satu dari 3 kondisi terakhir ini benar, maka akan dapat memotong arus sisa evaluasi.
JNK

Jawaban:

28

Jawaban Oleski salah. Untuk SQL Server 2008, INdaftar akan dire-refored ke serangkaian ORpernyataan. Mungkin berbeda di katakanlah MySQL.

Saya cukup yakin bahwa jika Anda membuat rencana eksekusi aktual untuk kedua pertanyaan Anda, itu akan sama.

Kemungkinan query kedua berjalan lebih cepat karena Anda menjalankannya kedua , dan query pertama sudah menarik semua halaman data dari database dan membayar biaya IO. Permintaan kedua mampu membaca semua data dari memori dan mengeksekusi jauh lebih cepat.

Memperbarui

Sumber aktual varians kemungkinan bahwa kueri tidak setara . Anda memiliki dua ORdaftar berbeda di bawah ini:

WHERE PV.Adm_Date BETWEEN @SD AND @ED
AND SO.svc_cd = 'PCO_REMFOLEY'
OR SO.svc_cd = 'PCO_INSRTFOLEY'
OR SO.svc_cd = 'PCO_INSTFOLEY'
OR SO.svc_cd = 'PCO_URIMETER'

dan kemudian

 WHERE OSM.ord_sts = 'DISCONTINUE'
    AND SO.svc_cd = 'PCO_REMFOLEY'
    OR SO.svc_cd = 'PCO_INSRTFOLEY'
    OR SO.svc_cd = 'PCO_INSTFOLEY'
    OR SO.svc_cd = 'PCO_URIMETER'

Dalam kedua WHEREklausa tersebut, prioritas operator (di mana AND ditangani sebelum OR) berarti bahwa logika aktual yang dijalankan oleh mesin adalah:

WHERE (ConditionA AND ConditionB)
OR ConditionC
OR ConditionD
OR ConditionE

Jika Anda mengganti ORdaftar dengan INekspresi, logikanya adalah:

WHERE ConditionA
AND (ConditionB OR ConditionC OR ConditionD OR ConditionE)

Yang sangat berbeda.

JNK
sumber
2
@MCP_infiltrator Nah itu masalahnya dengan membuat asumsi :) Anda benar-benar harus mendapatkan rencana exec aktual untuk kedua dan melihat apakah ada perbedaan, saya tidak berpikir akan ada.
JNK
4
Nah jika Anda memiliki pertanyaan DB tingkat lanjut, Anda juga dapat bertanya pada Administrator Database - pengungkapan penuh, saya seorang moderator di sana, tetapi jika ini adalah pertanyaan pengoptimalan SQL atau SQL lanjutan, kami memiliki banyak pakar, terutama untuk SQL Server
JNK
1
Saya hanya melihat dua rencana eksekusi dan keduanya sangat berbeda. Query dengan OR pernyataan memakan 68% dari biaya dalam Clustered Index Scan, di mana pernyataan IN adalah 26%, bersama dengan apa yang tampaknya menjadi kurang langkah eksekusi juga.
MCP_infiltrator
3
@MCP_infiltrator Tidak perlu, lihat komentar saya di posting asli Anda di atas. INtidak setara dengan ORs di atas Anda karena kondisi lain dalam WHEREklausa Anda di kueri yang sebenarnya. Pada dasarnya kueri akan menghasilkan hasil yang berbeda.
JNK
3
@MCP_infiltrator Tidak perlu memposting pertanyaan yang sama di DBA.SE, JNK telah menjawabnya (dan Anda akan mendapatkan jawaban serupa di sana.) Jika Anda ingin memindahkan ("bermigrasi") di sana, Anda selalu dapat menandai itu (pertanyaan Anda) menyebutkan di kotak komentar apa yang Anda inginkan. Mod akan berhati-hati.
ypercubeᵀᴹ
7

Cara terbaik untuk mengetahui adalah dengan melihat rencana permintaan aktual menggunakan sesuatu seperti EXPLAIN. Ini akan memberi tahu Anda apa yang sedang dilakukan DBMS, dan kemudian Anda bisa mendapatkan ide yang lebih baik mengapa lebih efisien.

Dengan itu, sistem DBMS sangat bagus dalam melakukan operasi antara dua tabel (seperti gabungan). Banyak waktu pengoptimal dihabiskan untuk bagian pertanyaan ini karena biasanya lebih mahal.

Misalnya, DBMS dapat mengurutkan INdaftar itu dan, menggunakan indeks aktif item_desc, filter hasilnya dengan sangat cepat. Anda tidak dapat melakukan pengoptimalan ketika Anda mendaftar banyak pilihan seperti pada contoh pertama.

Saat Anda menggunakan IN, Anda membuat tabel dadakan dan memfilter menggunakan teknik menggabungkan tabel yang lebih efisien ini.

EDIT : Saya memposting jawaban ini sebelum OP menyebutkan DBMS spesifik. Ini ternyata BUKAN bagaimana SQL Server memperlakukan kueri ini, tetapi mungkin valid untuk sistem DBMS lainnya. Lihat jawaban JNK untuk jawaban yang lebih spesifik dan akurat.

Oleksi
sumber
Saya akan membayangkan kardinalitas banyak hubungannya dengan itu. Itu INtidak akan begitu cepat jika itu adalah subselect dengan 100 catatan di dalamnya, atau seribu.
Robert Harvey
@ RobertTarvey Ya, itu mungkin benar, tapi saya juga tidak berharap itu akan menjadi jauh lebih buruk.
Oleksi
Terima kasih @ Oleksi saya tidak tahu bahwa DBMS akan membuat pernyataan IN daftar dadakan
MCP_infiltrator
1
-1 - Dalam SQL Server INpernyataan itu tidak dikonversi ke tabel, itu diperlakukan identik dengan serangkaian ORs.
JNK
2
@ Katana314 Jika EXPLAIN adalah kata kunci dalam SQL Server (yang digunakan OP), saya akan setuju dengan Anda, tetapi tidak jadi tidak relevan.
JNK