SQL IN () VEYA’ya karşı


23

Bugün yazdığım bir sorgu ile çalışıyordum gibi bir WHEREşey kullanmak yerine bir IN (malzeme listesi) filtresi kullanmak için yan tümce kodunu değiştirmek zorunda kaldı

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

Yukarıdakiler 15 dakika sürdü ve hiçbir şey geri dönmedi, ancak takip eden sonuç 1.5 dakika içinde belirlediğim sonucu verdi.

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

Bunu SQL'de yaptım ve IN (öğe listesi) neden OR deyiminden daha hızlı performans gösterdiğini merak ediyorum.

- EDIT - SQL Server 2008, bu bilgiyi bir yere koymadığım için özür dilerim.

ORİfadeleri kullanarak Sorgu tam olarak burada :

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

Teşekkür ederim,


10
Sorgu planına baktın mı?

1
Bu ÇOK uygulamaya özeldir. Hangi DBMS'yi kullanıyorsunuz?
James Anderson

Sorgu planına bakmadım, sorgunun kendine özgü olup olmadığını veya bunun her zaman bu şekilde çalışacağı gibi bir gerçek olup olmadığını bilmiyordum.
MCP_infiltrator

3
@MCP_infiltrator Mantık eşdeğer olmadığı için yürütme planları aynı olmaz. Kullanırken ORyukarıdaki gerçek sorguda yapmak gibi kısa devre motoru tanır. WHERE A AND B OR CA VE B yanlış olsa bile, C doğru ise doğru olarak değerlendirecektir. WHERE A and B OR C OR D OR E OR FYukarıda yaptığınız gibi söylerseniz , AND etkisiz hale getirilebilir. Gerçek eşdeğer mantık kapsülleyen ORbir grubu olarak kabul edilir, böylece parantez içinde yukarıdaki serisi: WHERE A AND (B OR C OR D OR E). Bu nasıl INtedavi edilir.
JNK

5
Daha ANDönce ele alınan SQL Server'daki operatör önceliği OR, bu nedenle yukarıdaki sorunuz eşdeğerdir, 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'yani son 3 koşuldan herhangi biri doğruysa, değerlendirmenin geri kalanını kısa devre yapabilecek demektir.
JNK

Yanıtlar:


28

Oleski'nin cevabı yanlış. SQL Server 2008 için, bir INliste bir dizi ORifadeye göre yeniden düzenlenir. MySQL de demek farklı olabilir.

Her iki sorunuz için de gerçek uygulama planları oluşturduysanız, bunların aynı olacağından oldukça eminim.

Her durumda, ikinci sorgu daha hızlı koştu , çünkü siz ikinci koştunuz ve ilk sorgu tüm veri sayfalarını veritabanından çoktan aldı ve IO maliyetini ödedi. İkinci sorgu tüm verileri bellekten okuyabildi ve çok daha hızlı bir şekilde çalıştırabildi.

Güncelleştirme

Gerçek değişimin kaynağı, sorguların eşdeğer olmaması muhtemeldir . ORAşağıda iki farklı listeniz var :

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'

ve sonra

 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'

Her iki WHEREmaddede de , operatör önceliği (VE'nin VEYA'dan önce işlendiği yer) motor tarafından çalıştırılan gerçek mantığın:

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

ORListeleri bir INifadeyle değiştirirseniz , mantık şöyle olacaktır:

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

Bu radikal olarak farklı.


2
@MCP_infiltrator Peki varsayımlarda sorun var :) Her ikisi için de gerçek uygulama planları almalı ve bir fark olup olmadığını görmelisiniz, olacağını sanmıyorum.
JNK

4
Eğer gelişmiş bir DB sorunuz varsa, ayrıca Veritabanı Yöneticilerine de sorabilirsiniz - tam açıklama, ben orada bir moderatörüm ama eğer gelişmiş bir SQL veya SQL optimizasyon sorusuysa, özellikle SQL Server için bir ton
uzmanımız var

1
Sadece iki yürütme planına baktım ve onlar çok farklı. OR ifadeleriyle yapılan sorgu, IN ifadesinin% 26 olduğu Kümelenmiş Dizin Taraması'nda maliyetin% 68'ini kapsıyor ve bunun yanında daha az yürütme adımı gibi görünüyor.
MCP_infiltrator

3
@MCP_infiltrator Gerek yok, en üstteki orijinal yayınınızdaki yorumlarıma bakın. fiili sorgudaki cümlenizdeki diğer koşullar nedeniyle yukarıda belirtilenlere INeşdeğer değildir . Temel olarak, sorgular farklı sonuçlar getirecektir. ORWHERE
JNK,

3
@MCP_infiltrator DBA.SE'de aynı soruyu göndermenize gerek yoktur, JNK cevap verdi (ve orada benzer cevaplar alacaksınız.) Oraya taşımak istiyorsanız ("geçir") yine de işaretleyebilirsiniz. (sorunuz) yorum kutusuna ne istediğinizi söyleyerek. Mods ilgilenecek.
ypercubeᵀᴹ

7

Anlamanın en iyi yolu, gerçek bir sorgu planına benzer bir şey kullanarak bakmaktır EXPLAIN. Bu, DBMS'nin tam olarak ne yaptığını size söylemelidir ve bunun neden daha verimli olduğu konusunda daha iyi bir fikir edinebilirsiniz.

Bununla birlikte, DBMS sistemleri iki masa (birleşme gibi) arasında işlem yapmakta gerçekten iyidir. Optimize Edici'nin zamanının çoğu, sorguların bu kısımlarına harcanır, çünkü genellikle daha pahalıdırlar.

Örneğin, DBMS bu INlisteyi sıralayabilir ve bir dizin kullanarak item_descsonuçları çok hızlı bir şekilde filtreleyebilir. İlk örnekte olduğu gibi bir grup seçimi listelerken bu optimizasyonu yapamazsınız.

Kullandığınızda IN, bir doğaçlama tablo yapma ve bu daha verimli tablo birleştirme teknikleri kullanılarak filtre edilir.

EDIT : OP bu özel DBMS'den bahsetmeden önce bu cevabı gönderdim. Bu, SQL Server'ın bu sorguyu nasıl ele aldığı DEĞİLDİR, fakat diğer DBMS sistemleri için geçerli olabilir. Daha spesifik ve doğru bir cevap için JNK'nın cevabına bakınız .


Kardinalitenin bununla çok ilgisi olduğunu hayal ediyorum. Yani IN100 içinde kayıtları veya bin bir alt seçim olsaydı bu kadar hızlı olmazdı.
Robert Harvey,

@RobertHarvey Evet, muhtemelen doğru, ama ben de o kadar da kötü olmasını beklemiyordum.
Oleksi,

Thanks @Oleksi DBMS IN deyimini doğaçlama bir liste yapacağını bilmiyordum
MCP_infiltrator 12:13

1
-1 - SQL Server'da, INifade bir tabloya dönüştürülmez, aynı ORs serisi ile aynı şekilde ele alınır .
JNK

2
@ Katana314 Eğer EXPLAIN, SQL Server'da (OP kullandığı) bir anahtar kelime olsaydı, sizinle aynı fikirdeyim, ama o kadar alakalı değil.
JNK
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.