SQL IN () versus OR

23

Estaba trabajando con una consulta que escribí hoy, tenía que cambiar el código de la WHEREcláusula para usar un filtro IN (lista de cosas) en lugar de usar algo como

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

Lo anterior se ejecutó durante 15 minutos y no devolvió nada, sin embargo, lo siguiente me dio mi conjunto de resultados en 1,5 minutos

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

Hice esto en SQL y me pregunto por qué el IN (lista de elementos) se desempeñó mucho más rápido que la instrucción OR.

- EDITAR - SQL Server 2008, me disculpo por no poner esta información en primer lugar.

Aquí está la consulta en su totalidad utilizando las ORdeclaraciones:

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

Gracias,

MCP_infiltrator
fuente
10
¿Has mirado el plan de consulta?
1
Esto es MUY específico de implementación. ¿Qué DBMS estás usando?
James Anderson
No miré el plan de consulta, no sabía si se trataba de una consulta específica o si era una cuestión de hecho, ya que esto siempre funcionaría de esta manera.
MCP_infiltrator
3
@MCP_infiltrator Por lo tanto, los planes de ejecución no serán equivalentes porque la lógica no es equivalente. Cuando se usa ORcomo lo hace en la consulta real anterior, permite que el motor se cortocircuite. WHERE A AND B OR Cse evaluará como verdadero incluso si A y B son falsos, si C es verdadero. Si dices WHERE A and B OR C OR D OR E OR Fcomo lo hiciste anteriormente, AND se puede factorizar. La lógica equivalente real sería encapsular las ORseries anteriormente en paréntesis para que se tratan como un conjunto: WHERE A AND (B OR C OR D OR E). Así es como INse trata.
JNK
55
La precedencia del operador en SQL Server especificó que ANDse maneja antes OR, por lo que su consulta anterior es equivalente a lo 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'que significa que si alguna de las últimas 3 condiciones es verdadera, podrá cortocircuitar el resto de la evaluación.
JNK

Respuestas:

28

La respuesta de Oleski es incorrecta. Para SQL Server 2008, una INlista se refactoriza a una serie de ORdeclaraciones. Puede ser diferente en decir MySQL.

Estoy bastante seguro de que si generara planes de ejecución reales para ambas consultas, serían idénticas.

Con toda probabilidad, la segunda consulta se ejecutó más rápido porque la ejecutó en segundo lugar , y la primera consulta ya había extraído todas las páginas de datos de la base de datos y pagado el costo de IO. La segunda consulta pudo leer todos los datos de la memoria y ejecutarse mucho más rápido.

Actualizar

La fuente real de la variación es probable que las consultas no sean equivalentes . Tienes dos ORlistas diferentes a continuación:

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'

y después

 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'

En ambas WHEREcláusulas, la precedencia del operador (donde AND se maneja antes que OR) significa que la lógica real ejecutada por el motor es:

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

Si reemplaza las ORlistas con una INexpresión, la lógica será:

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

Lo cual es radicalmente diferente.

JNK
fuente
2
@MCP_infiltrator Bueno, ese es el problema con hacer suposiciones :) Realmente deberías obtener planes ejecutivos reales para ambos y ver si hay una diferencia, no creo que la haya.
JNK
44
Bueno, si tiene una pregunta avanzada sobre la base de datos, también puede preguntar a los administradores de bases de datos : divulgación completa, soy un moderador allí, pero si se trata de una pregunta avanzada de SQL o optimización de SQL, tenemos un montón de expertos, especialmente para SQL Server
JNK
1
Acabo de mirar los dos planes de ejecución y son muy diferentes. La consulta con las declaraciones OR ocupa el 68% del costo en el Análisis de índice agrupado, donde la declaración IN es del 26%, junto con lo que parecen ser también menos pasos de ejecución.
MCP_infiltrator
3
@MCP_infiltrator No es necesario, vea mis comentarios en su publicación original en la parte superior. INno es equivalente a su ORs anterior debido a las otras condiciones en su WHEREcláusula en la consulta real. Básicamente las consultas devolverán resultados diferentes.
JNK
3
@MCP_infiltrator No hay necesidad de publicar una pregunta idéntica en DBA.SE, JNK la ha respondido (y obtendrá respuestas similares allí). Sin embargo, si desea moverla ("migrarla"), siempre puede marcarla. (su pregunta) mencionando en el cuadro de comentarios lo que desea. Las modificaciones se encargarán.
ypercubeᵀᴹ
7

La mejor manera de saberlo es mirar el plan de consulta real usando algo como EXPLAIN. Esto debería decirle exactamente lo que está haciendo el DBMS, y luego puede tener una mejor idea de por qué es más eficiente.

Dicho esto, los sistemas DBMS son realmente buenos para realizar operaciones entre dos tablas (como uniones). Gran parte del tiempo del optimizador se gasta en estas partes de las consultas porque generalmente son más caras.

Por ejemplo, el DBMS podría ordenar esa INlista y, utilizando un índice item_desc, filtrar los resultados muy rápidamente. No puede hacer esa optimización cuando enumera un montón de selecciones como en el primer ejemplo.

Cuando lo usa IN, está haciendo una tabla improvisada y filtrando usando estas técnicas de combinación de tablas más eficientes.

EDITAR : publiqué esta respuesta antes de que OP mencionara el DBMS específico. Esto resulta que NO es cómo SQL Server trata esta consulta, pero podría ser válido para otros sistemas DBMS. Consulte la respuesta de JNK para obtener una respuesta más específica y precisa.

Oleksi
fuente
Me imagino que la cardinalidad tiene mucho que ver con eso. Eso INno sería tan rápido si fuera una subselección con 100 registros, o mil.
Robert Harvey
@RobertHarvey Sí, probablemente sea cierto, pero tampoco esperaría que fuera mucho peor.
Oleksi
Gracias @Oleksi No sabía que el DBMS haría que la declaración IN fuera una lista improvisada
MCP_infiltrator
1
-1: en SQL Server, la INinstrucción no se convierte en una tabla, se trata de manera idéntica a una serie de ORs.
JNK
2
@ Katana314 Si EXPLAIN fuera una palabra clave en SQL Server (que está utilizando el OP), estaría de acuerdo con usted, pero no es así, no es relevante.
JNK