SQL IN () versus OR

23

Ich arbeitete mit einer Abfrage, die ich heute schrieb, musste den Code aus der WHEREKlausel ändern , um einen IN-Filter (List of Stuff) zu verwenden, anstatt so etwas zu verwenden

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

Das obige lief 15 Minuten lang und gab nichts zurück, aber das Folgende gab mir meine Ergebnismenge in 1,5 Minuten

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

Ich habe dies in SQL getan und frage mich, warum die IN-Anweisung (Liste der Elemente) so viel schneller ausgeführt wurde als die OR-Anweisung.

- BEARBEITEN - SQL Server 2008, ich entschuldige mich dafür, dass ich diese Informationen nicht in erster Linie eingegeben habe.

Hier ist die Abfrage in ihrer Gesamtheit mit den ORAnweisungen:

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

Vielen Dank,

MCP_infiltrator
quelle
10
Haben Sie sich den Abfrageplan angesehen?
1
Dies ist SEHR implementierungsspezifisch. Welches DBMS verwenden Sie?
James Anderson
Ich habe mir den Abfrageplan nicht angesehen, ich wusste nicht, ob dieser abfragespezifisch war oder ob es sich um eine Tatsache handelte, da dies immer auf diese Weise funktionieren würde.
MCP_infiltrator
3
@MCP_infiltrator Die Ausführungspläne sind also nicht gleichwertig, da die Logik nicht gleichwertig ist. Wenn ORSie wie in der obigen Abfrage verwenden, können Sie den Motor kurzschließen. WHERE A AND B OR Cwird als wahr ausgewertet, auch wenn A UND B falsch sind, wenn C wahr ist. Wenn Sie sagen, WHERE A and B OR C OR D OR E OR Fwie Sie es oben tun, AND können Sie das ausklammern. Die tatsächliche äquivalente Logik würde die einzukapseln ORSerie oben in Klammern , so dass sie als ein Satz behandelt werden: WHERE A AND (B OR C OR D OR E). So INwird behandelt.
JNK
5
Die Operatorpräzision in SQL Server wurde so festgelegt, dass sie ANDzuvor behandelt ORwurde. Ihre obige Abfrage entspricht also, 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'dass der Rest der Auswertung kurzgeschlossen werden kann, wenn eine der letzten drei Bedingungen zutrifft.
JNK

Antworten:

28

Oleskis Antwort ist falsch. Für SQL Server 2008 wird eine INListe in eine Reihe von ORAnweisungen umgestaltet . Dies kann beispielsweise in MySQL anders sein.

Ich bin mir ziemlich sicher, dass die tatsächlichen Ausführungspläne für beide Abfragen identisch sind.

Höchstwahrscheinlich lief die zweite Abfrage schneller, weil Sie sie als zweite ausgeführt haben , und die erste Abfrage hat bereits alle Datenseiten aus der Datenbank abgerufen und die E / A-Kosten bezahlt. Die zweite Abfrage war in der Lage, alle Daten aus dem Speicher zu lesen und viel schneller auszuführen.

Aktualisieren

Die tatsächliche Quelle der Varianz ist wahrscheinlich, dass die Abfragen nicht gleichwertig sind . Sie haben zwei verschiedene ORListen:

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'

und später

 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'

In diesen beiden WHEREAbschnitten bedeutet die Operatorpräzision (wobei AND vor OR behandelt wird), dass die tatsächliche Logik, die von der Engine ausgeführt wird, Folgendes ist:

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

Wenn Sie die ORListen durch einen INAusdruck ersetzen, lautet die Logik:

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

Welches ist radikal anders.

JNK
quelle
2
@MCP_infiltrator Nun, das ist das Problem, wenn man Annahmen trifft :) Man sollte sich wirklich konkrete Ausführungspläne für beide holen und sehen, ob es einen Unterschied gibt, ich glaube nicht, dass es einen geben wird.
JNK
4
Wenn Sie eine fortgeschrittene DB-Frage haben, können Sie sich auch an Datenbankadministratoren wenden - vollständige Offenlegung, ich bin dort ein Moderator, aber wenn es sich um eine fortgeschrittene SQL- oder SQL-Optimierungsfrage handelt, haben wir eine Menge Experten, insbesondere für SQL Server
JNK
1
Ich habe mir nur die beiden Ausführungspläne angesehen und sie sind sehr unterschiedlich. Die Abfrage mit den OR-Anweisungen macht 68% der Kosten in Clustered Index Scan aus, wobei die IN-Anweisung 26% beträgt, zusammen mit scheinbar weniger Ausführungsschritten.
MCP_infiltrator
3
@MCP_infiltrator Keine Notwendigkeit, siehe meine Kommentare zu Ihrem ursprünglichen Beitrag oben. INist ORaufgrund der anderen Bedingungen in Ihrer WHEREKlausel in der eigentlichen Abfrage nicht gleichbedeutend mit den oben genannten . Grundsätzlich werden die Abfragen unterschiedliche Ergebnisse zurückgeben.
JNK
3
@MCP_infiltrator Bei DBA.SE muss keine identische Frage gestellt werden. JNK hat sie beantwortet (und Sie erhalten dort ähnliche Antworten.) Wenn Sie sie jedoch dorthin verschieben ("migrieren") möchten, können Sie sie jederzeit kennzeichnen (Ihre Frage) Erwähnen Sie im Kommentarfeld, was Sie möchten. Die Mods kümmern sich darum.
Ypercubeᵀᴹ
7

Der beste Weg, dies zu erkennen, besteht darin, sich den tatsächlichen Abfrageplan mit etwas Ähnlichem anzusehen EXPLAIN. Dies sollte Ihnen genau sagen, was das DBMS tut, und dann können Sie eine viel bessere Vorstellung davon bekommen, warum es effizienter ist.

Trotzdem können DBMS-Systeme wirklich gut Operationen zwischen zwei Tabellen ausführen (wie Joins). Ein großer Teil der Zeit des Optimierers wird für diese Teile der Abfragen aufgewendet, da sie im Allgemeinen teurer sind.

Beispielsweise könnte das DBMS diese INListe sortieren und mithilfe eines Indexes item_descdie Ergebnisse sehr schnell filtern. Sie können diese Optimierung nicht durchführen, wenn Sie eine Reihe von Auswahlen wie im ersten Beispiel auflisten.

Wenn Sie verwenden IN, erstellen Sie eine spontane Tabelle und filtern mithilfe dieser effizienteren Tabellenkombinationstechniken.

EDIT : Ich habe diese Antwort gepostet, bevor OP das spezifische DBMS erwähnte. Es stellt sich heraus, dass SQL Server diese Abfrage NICHT behandelt, sondern möglicherweise für andere DBMS-Systeme gültig ist. Siehe JNK Antwort für eine bestimmte, genaue Antwort.

Oleksi
quelle
Ich würde mir vorstellen, dass die Kardinalität viel damit zu tun hat. Das INwäre nicht so schnell, wenn es sich um eine Unterauswahl mit 100 Datensätzen oder tausend Datensätzen handeln würde.
Robert Harvey
@RobertHarvey Ja, das stimmt wahrscheinlich, aber ich würde auch nicht erwarten, dass es so viel schlimmer wird.
Oleksi
Danke @Oleksi Ich wusste nicht, dass das DBMS die IN-Anweisung zu einer
spontanen
1
-1 - In SQL Server wird die INAnweisung nicht in eine Tabelle konvertiert, sondern wie eine Reihe von ORs behandelt.
JNK
2
@ Katana314 Wenn EXPLAIN ein Schlüsselwort in SQL Server wäre (das vom OP verwendet wird), stimme ich Ihnen zu, aber es ist nicht so, dass es nicht relevant ist.
JNK