SQL IN () contre OR

23

Je travaillais avec une requête que j'ai écrite aujourd'hui a dû changer le code de la WHEREclause pour utiliser un filtre IN (liste de choses) au lieu d'utiliser quelque chose comme

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

Ce qui précède a duré 15 minutes et n'a rien retourné, mais ce qui suit m'a donné mon jeu de résultats en 1,5 minute

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

J'ai fait cela en SQL et je me demande pourquoi l'IN (liste des éléments) a fonctionné tellement plus vite que l'instruction OR.

- EDIT - SQL Server 2008, je m'excuse de ne pas avoir mis ces informations en premier lieu.

Voici la requête dans son intégralité à l'aide des ORinstructions:

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

Je vous remercie,

MCP_infiltrator
la source
10
Avez-vous regardé le plan de requête?
1
Ceci est TRÈS spécifique à l'implémentation. Quel SGBD utilisez-vous?
James Anderson
Je n'ai pas regardé le plan de requête, je ne savais pas si c'était spécifique à la requête ou si c'était une question de fait, car cela fonctionnerait toujours de cette façon.
MCP_infiltrator
3
@MCP_infiltrator Les plans d'exécution ne seront donc pas équivalents car la logique n'est pas équivalente. Lorsque vous utilisez ORcomme vous le faites dans la requête ci-dessus, vous autorisez le moteur à court-circuiter. WHERE A AND B OR Csera évalué à vrai même si A ET B sont faux, si C est vrai. Si vous dites WHERE A and B OR C OR D OR E OR Fcomme vous le faites ci-dessus, le AND peut être éliminé. La logique réelle équivalent intégrerait les ORséries ci - dessus entre parenthèses ils sont traités comme un ensemble: WHERE A AND (B OR C OR D OR E). C'est ainsi que l' INon traite un.
JNK
5
La priorité de l'opérateur dans SQL Server spécifiée ANDest gérée avant OR, donc votre requête ci-dessus est équivalente à 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'ce qui signifie que si l'une des 3 dernières conditions est vraie, elle pourra court-circuiter le reste de l'évaluation.
JNK

Réponses:

28

La réponse d'Oleski est incorrecte. Pour SQL Server 2008, une INliste est refactorisée en une série d' ORinstructions. Cela peut être différent, disons MySQL.

Je suis assez certain que si vous génériez des plans d'exécution réels pour vos deux requêtes, ils seraient identiques.

Selon toute vraisemblance, la deuxième requête s'est exécutée plus rapidement car vous l'avez exécutée en deuxième , et la première requête avait déjà extrait toutes les pages de données de la base de données et payé le coût d'E / S. La deuxième requête a pu lire toutes les données de la mémoire et s'exécuter beaucoup plus rapidement.

Mise à jour

La source réelle de la variance est probablement que les requêtes ne sont pas équivalentes . Vous avez deux ORlistes différentes ci-dessous:

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'

et ensuite

 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'

Dans ces deux WHEREclauses, la priorité de l'opérateur (où AND est géré avant OR) signifie que la logique réelle exécutée par le moteur est:

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

Si vous remplacez les ORlistes par une INexpression, la logique sera:

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

Ce qui est radicalement différent.

JNK
la source
2
@MCP_infiltrator Eh bien, c'est le problème avec les hypothèses :) Vous devriez vraiment obtenir des plans d'exécution réels pour les deux et voir s'il y a une différence, je ne pense pas qu'il y en aura.
JNK
4
Eh bien, si vous avez une question DB avancée, vous pouvez également demander aux administrateurs de base de données - divulgation complète, je suis un modérateur là-bas, mais s'il s'agit d'une question avancée SQL ou d'optimisation SQL, nous avons une tonne d'experts, en particulier pour SQL Server
JNK
1
Je viens de regarder les deux plans d'exécution et ils sont très différents. La requête avec les instructions OR prend 68% du coût dans l'analyse d'index clusterisé, où l'instruction IN est de 26%, ainsi que ce qui semble être moins d'étapes d'exécution.
MCP_infiltrator
3
@MCP_infiltrator Pas besoin, voir mes commentaires sur votre post d'origine en haut. INn'est pas équivalent aux vôtres ORci-dessus en raison des autres conditions de votre WHEREclause dans la requête réelle. Fondamentalement, les requêtes renverront des résultats différents.
JNK
3
@MCP_infiltrator Il n'est pas nécessaire de poster une question identique sur DBA.SE, JNK y a répondu (et vous obtiendrez des réponses similaires là-bas). (votre question) en mentionnant dans la zone de commentaire ce que vous voulez. Les mods s'en chargeront.
ypercubeᵀᴹ
7

La meilleure façon de le savoir est de regarder le plan de requête réel en utilisant quelque chose comme EXPLAIN. Cela devrait vous dire exactement ce que fait le SGBD, et vous pourrez alors avoir une bien meilleure idée de pourquoi il est plus efficace.

Cela dit, les systèmes SGBD sont vraiment bons pour effectuer des opérations entre deux tables (comme les jointures). Une grande partie du temps de l'optimiseur est consacrée à ces parties des requêtes car elles sont généralement plus chères.

Par exemple, le SGBD pourrait trier cette INliste et, à l'aide d'un index item_desc, filtrer les résultats très rapidement. Vous ne pouvez pas faire cette optimisation lorsque vous listez un tas de sélections comme dans le premier exemple.

Lorsque vous utilisez IN, vous créez une table impromptue et un filtrage à l'aide de ces techniques de combinaison de tables plus efficaces.

EDIT : J'ai posté cette réponse avant que OP ne mentionne le SGBD spécifique. Il s'avère que ce n'est PAS ainsi que SQL Server traite cette requête, mais cela peut être valable pour d'autres systèmes SGBD. Voir la réponse de JNK pour une réponse plus précise et plus précise.

Oleksi
la source
J'imagine que la cardinalité y est pour beaucoup. Ce INne serait pas si rapide s'il s'agissait d'une sous-sélection contenant 100 enregistrements, ou un millier.
Robert Harvey
@RobertHarvey Oui, c'est probablement vrai, mais je ne m'attendrais pas à ce que ce soit bien pire non plus.
Oleksi
Merci @Oleksi Je ne savais pas que le SGBD ferait de l'instruction IN une liste impromptue
MCP_infiltrator
1
-1 - Dans SQL Server, l' INinstruction n'est pas convertie en table, elle est traitée de manière identique à une série de ORs.
JNK
2
@ Katana314 Si EXPLAIN était un mot-clé dans SQL Server (que l'OP utilise), je serais d'accord avec vous, mais ce n'est pas le cas, donc ce n'est pas pertinent.
JNK