Je travaillais avec une requête que j'ai écrite aujourd'hui a dû changer le code de la WHERE
clause 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 OR
instructions:
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,
OR
comme vous le faites dans la requête ci-dessus, vous autorisez le moteur à court-circuiter.WHERE A AND B OR C
sera évalué à vrai même si A ET B sont faux, si C est vrai. Si vous ditesWHERE A and B OR C OR D OR E OR F
comme vous le faites ci-dessus, leAND
peut être éliminé. La logique réelle équivalent intégrerait lesOR
sé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'IN
on traite un.AND
est gérée avantOR
, 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.Réponses:
La réponse d'Oleski est incorrecte. Pour SQL Server 2008, une
IN
liste est refactorisée en une série d'OR
instructions. 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
OR
listes différentes ci-dessous:et ensuite
Dans ces deux
WHERE
clauses, 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:Si vous remplacez les
OR
listes par uneIN
expression, la logique sera:Ce qui est radicalement différent.
la source
IN
n'est pas équivalent aux vôtresOR
ci-dessus en raison des autres conditions de votreWHERE
clause dans la requête réelle. Fondamentalement, les requêtes renverront des résultats différents.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
IN
liste et, à l'aide d'un indexitem_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.
la source
IN
ne serait pas si rapide s'il s'agissait d'une sous-sélection contenant 100 enregistrements, ou un millier.IN
instruction n'est pas convertie en table, elle est traitée de manière identique à une série deOR
s.