SQL IN() contre OU
https://softwareengineering.stackexchange.com/questions/204559
Question
Je travaillais avec une requête que j'ai écrite aujourd'hui et j'ai dû changer le code du WHERE
clause pour utiliser un filtre IN(list of stuff) 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 renvoyé, mais ce qui suit m'a donné mon résultat défini 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 d'éléments) a fonctionné beaucoup plus rapidement que l'instruction OR.
- Edit - SQL Server 2008, je m'excuse de ne pas avoir mis ce peu d'informations en premier lieu.
Voici la requête dans son intégralité en utilisant le OR
déclarations:
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
Merci,
La solution
La réponse d'Oleski est incorrecte.Pour SQL Server 2008, un IN
la liste est refactorisée en une série de OR
déclarations.Cela peut être différent, par exemple, avec MySQL.
Je suis presque 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 parce que tu l'as couru 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 des 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 véritable source de la variance est probablement due au fait que les requêtes sont pas équivalent.Vous avez deux différents OR
listes 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 les deux WHERE
clauses, la priorité des opérateurs (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 le OR
des listes avec un IN
expression, la logique sera :
WHERE ConditionA
AND (ConditionB OR ConditionC OR ConditionD OR ConditionE)
Ce qui est radicalement différent.
Autres conseils
La meilleure façon de le savoir est d'examiner 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 efficaces 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 coûteuses.
Par exemple, le SGBD pourrait trier cela IN
liste et, en utilisant un index sur item_desc
, filtrez les résultats très rapidement.Vous ne pouvez pas effectuer cette optimisation lorsque vous répertoriez un ensemble de sélections comme dans le premier exemple.
Quand vous utilisez IN
, vous créez un tableau impromptu et filtrez à l'aide de ces techniques de combinaison de tableaux plus efficaces.
MODIFIER:J'ai posté cette réponse avant qu'OP ne mentionne le SGBD spécifique.Il s'avère que ce n'est PAS la façon dont SQL Server traite cette requête, mais cela pourrait ê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.