Saya bekerja dengan kueri yang saya tulis hari ini harus mengubah kode dari WHERE
klausa 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 OR
pernyataan:
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,
OR
seperti yang Anda lakukan dalam permintaan aktual di atas, Anda mengizinkan engine mengalami hubungan pendek.WHERE A AND B OR C
akan mengevaluasi ke true bahkan jika A DAN B salah, jika C benar. Jika Anda mengatakanWHERE A and B OR C OR D OR E OR F
seperti yang Anda lakukan di atas,AND
dapat diperhitungkan. Setara dengan logika yang sebenarnya akan merangkumOR
seri di atas dalam kurung sehingga mereka diperlakukan sebagai satu set:WHERE A AND (B OR C OR D OR E)
. Beginilah cara seorangIN
diperlakukan.AND
ditangani sebelumnyaOR
, sehingga kueri Anda di atas setara denganWHERE (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.Jawaban:
Jawaban Oleski salah. Untuk SQL Server 2008,
IN
daftar akan dire-refored ke serangkaianOR
pernyataan. 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
OR
daftar berbeda di bawah ini:dan kemudian
Dalam kedua
WHERE
klausa tersebut, prioritas operator (di mana AND ditangani sebelum OR) berarti bahwa logika aktual yang dijalankan oleh mesin adalah:Jika Anda mengganti
OR
daftar denganIN
ekspresi, logikanya adalah:Yang sangat berbeda.
sumber
IN
tidak setara denganOR
s di atas Anda karena kondisi lain dalamWHERE
klausa Anda di kueri yang sebenarnya. Pada dasarnya kueri akan menghasilkan hasil yang berbeda.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
IN
daftar itu dan, menggunakan indeks aktifitem_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.
sumber
IN
tidak akan begitu cepat jika itu adalah subselect dengan 100 catatan di dalamnya, atau seribu.IN
pernyataan itu tidak dikonversi ke tabel, itu diperlakukan identik dengan serangkaianOR
s.