SQL IN () a OR

23

Pracowałem z zapytaniem, które napisałem dzisiaj, musiałem zmienić kod z WHEREklauzuli, aby użyć filtra IN (lista rzeczy) zamiast używać czegoś w rodzaju

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

Powyższe działało przez 15 minut i nic nie zwróciło, ale poniższe wyniki dały mi zestaw wyników w 1,5 minuty

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

Zrobiłem to w SQL i zastanawiam się, dlaczego IN (lista elementów) działał o wiele szybciej niż instrukcja OR.

- EDYCJA - SQL Server 2008, przepraszam, że nie umieściłem tej części informacji na pierwszym miejscu.

Oto zapytanie w całości przy użyciu ORinstrukcji:

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

Dziękuję Ci,

MCP_infiltrator
źródło
10
Czy spojrzałeś na plan zapytań?
1
Jest to BARDZO specyficzne dla implementacji. Z którego DBMS korzystasz?
James Anderson
Nie spojrzałem na plan zapytań, nie wiedziałem, czy jest to zapytanie specyficzne, czy też faktyczne, ponieważ w ten sposób zawsze działałoby w ten sposób.
MCP_infiltrator
3
@MCP_infiltrator Plany wykonania nie będą równoważne, ponieważ logika nie jest równoważna. Używając ORtak jak w powyższym zapytaniu, pozwalasz na zwarcie silnika. WHERE A AND B OR Coceni prawdę, nawet jeśli A i B są fałszywe, jeśli C jest prawdą. Jeśli powiesz WHERE A and B OR C OR D OR E OR Ftak jak powyżej, AND możesz to rozłożyć na czynniki. Rzeczywista równoważne logika kapsułkowania ORszereg powyżej w nawiasach są więc traktowane jako zestawy WHERE A AND (B OR C OR D OR E). Tak się INleczy.
JNK
5
Określona przez operatora pozycja w programie SQL Server, która ANDjest obsługiwana wcześniej OR, więc powyższe zapytanie jest równoważne, 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'co oznacza, że ​​jeśli którykolwiek z 3 ostatnich warunków jest spełniony, będzie w stanie zewrzeć resztę oceny.
JNK

Odpowiedzi:

28

Odpowiedź Oleskiego jest nieprawidłowa. W przypadku SQL Server 2008 INlista jest refaktoryzowana do szeregu ORinstrukcji. Może być inaczej, powiedzmy MySQL.

Jestem całkiem pewien, że jeśli wygenerowałbyś rzeczywiste plany wykonania dla obu zapytań, byłyby one identyczne.

Najprawdopodobniej drugie zapytanie przebiegło szybciej, ponieważ uruchomiono je na drugim miejscu , a pierwsze zapytanie już wyciągnęło wszystkie strony danych z bazy danych i pokryło koszty zamówienia. Drugie zapytanie było w stanie odczytać wszystkie dane z pamięci i wykonać je znacznie szybciej.

Aktualizacja

Rzeczywiste źródło wariancji prawdopodobnie nie jest równoważne z zapytaniami . Masz dwie różne ORlisty poniżej:

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'

i później

 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'

W obu tych WHEREklauzulach operator ma pierwszeństwo (gdzie AND jest obsługiwane przed OR) oznacza, że ​​rzeczywista logika uruchomiona przez silnik to:

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

Jeśli zastąpisz ORlisty INwyrażeniem, logika będzie wyglądać następująco:

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

Co jest zupełnie inne.

JNK
źródło
2
@MCP_infiltrator Cóż, to jest problem z przyjmowaniem założeń :) Naprawdę powinieneś dostać rzeczywiste plany wykonania dla obu i zobaczyć, czy jest jakaś różnica, nie sądzę, że będzie.
JNK
4
Cóż, jeśli masz zaawansowane pytanie DB, możesz również zapytać Administratorów baz danych - pełne ujawnienie, jestem tam moderatorem, ale jeśli jest to zaawansowane pytanie dotyczące SQL lub optymalizacji SQL, mamy mnóstwo ekspertów, szczególnie dla SQL Server
JNK
1
Właśnie spojrzałem na dwa plany wykonania i są one zupełnie różne. Kwerenda z instrukcjami OR zajmuje 68% kosztu w skanowaniu indeksu klastrowego, gdzie instrukcja IN wynosi 26%, a także, co wydaje się, mniejszą liczbą kroków wykonania.
MCP_infiltrator
3
@MCP_infiltrator Nie ma potrzeby, patrz moje komentarze do twojego oryginalnego postu na górze. INnie jest równoważne z ORpowyższymi ze względu na inne warunki w WHEREklauzuli w rzeczywistym zapytaniu. Zasadniczo zapytania zwrócą różne wyniki.
JNK
3
@MCP_infiltrator Nie ma potrzeby publikowania identycznego pytania w DBA.SE, JNK odpowiedział na nie (i tam znajdziesz podobne odpowiedzi.) Jeśli jednak chcesz przenieść („migrować”) tam, zawsze możesz je oflagować (twoje pytanie) wspominając w polu komentarza, co chcesz. Mody się zajmą.
ypercubeᵀᴹ
7

Najlepszym sposobem na to jest sprawdzenie rzeczywistego planu zapytań przy użyciu czegoś takiego EXPLAIN. To powinno dokładnie powiedzieć, co robi DBMS, a następnie możesz uzyskać znacznie lepszy pomysł, dlaczego jest bardziej wydajny.

To powiedziawszy, systemy DBMS są naprawdę dobre w wykonywaniu operacji między dwiema tabelami (jak sprzężenia). Dużo czasu optymalizatora spędza się na tych częściach zapytań, ponieważ są one na ogół droższe.

Na przykład DBMS może sortować tę INlistę i, używając indeksu item_desc, bardzo szybko filtrować wyniki. Nie możesz wykonać tej optymalizacji, gdy podajesz listę wybranych opcji, jak w pierwszym przykładzie.

Kiedy używasz IN, tworzysz zaimprowizowaną tabelę i filtrujesz za pomocą tych bardziej wydajnych technik łączenia tabel.

EDYCJA : Opublikowałem tę odpowiedź, zanim OP wspomniał o konkretnym DBMS. Okazuje się, że NIE jest to, w jaki sposób SQL Server traktuje to zapytanie, ale może być poprawne dla innych systemów DBMS. Zobacz odpowiedź JNK, aby uzyskać bardziej szczegółową i dokładną odpowiedź.

Oleksi
źródło
Wyobrażam sobie, że liczność ma z tym wiele wspólnego. Że INnie będzie tak szybko, jakby to był SUBSELECT 100 zapisów w nim, czy tysiąc.
Robert Harvey
@RobertHarvey Tak, to prawdopodobnie prawda, ale nie spodziewałbym się, że będzie o wiele gorzej.
Oleksi
Dzięki @Oleksi Nie wiedziałem, że DBMS sprawi, że instrukcja IN stanie się listą improwizowaną
MCP_infiltrator
1
-1 - W SQL Server INinstrukcja nie jest konwertowana na tabelę, jest traktowana identycznie jak seria ORs.
JNK
2
@ Katana314 Gdyby EXPLAIN był słowem kluczowym w SQL Server (którego używa OP), zgodziłbym się z tobą, ale to nie tak, że to nie jest istotne.
JNK