SQL IN() versus OU
https://softwareengineering.stackexchange.com/questions/204559
Pergunta
Eu estava trabalhando com uma consulta que escrevi hoje tive que alterar o código do WHERE
cláusula para usar um filtro IN(lista de coisas) em vez de usar algo como
item_desc = 'item 1'
OR item_desc = 'item 2'
OR item_desc = 'item 3'
OR item_desc = 'item 4'
O procedimento acima durou 15 minutos e não retornou nada, mas o seguinte me deu meu resultado definido em 1,5 minutos
item_desc IN (
'item 1'
,'item 2'
,'item 3'
,'item 4'
)
Fiz isso em SQL e estou me perguntando por que IN (lista de itens) teve um desempenho muito mais rápido que a instrução OR.
- Editar- SQL Server 2008, peço desculpas por não colocar esse pouco de informação em primeiro lugar.
Aqui está a consulta na íntegra usando o OR
declarações:
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
Obrigado,
Solução
A resposta de Oleski está incorreta.Para o SQL Server 2008, um IN
lista é refatorada para uma série de OR
declarações.Pode ser diferente, digamos, no MySQL.
Tenho quase certeza de que se você gerasse planos de execução reais para ambas as consultas, eles seriam idênticos.
É muito provável que a segunda consulta tenha sido executada mais rapidamente porque você executou em segundo lugar, e a primeira consulta já extraiu todas as páginas de dados do banco de dados e pagou o custo de IO.A segunda consulta foi capaz de ler todos os dados da memória e executar muito mais rápido.
Atualizar
A origem real da variação é provável que as consultas sejam não equivalente.Você tem dois diferentes OR
listas abaixo:
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'
e depois
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'
Em ambos aqueles WHERE
cláusulas, a precedência do operador (onde AND é tratado antes de OR) significa que a lógica real executada pelo mecanismo é:
WHERE (ConditionA AND ConditionB)
OR ConditionC
OR ConditionD
OR ConditionE
Se você substituir o OR
listas com um IN
expressão, a lógica será:
WHERE ConditionA
AND (ConditionB OR ConditionC OR ConditionD OR ConditionE)
O que é radicalmente diferente.
Outras dicas
A melhor maneira de saber é observar o plano de consulta real usando algo como EXPLAIN
.Isso deve dizer exatamente o que o SGBD está fazendo, e então você poderá ter uma ideia muito melhor de por que ele é mais eficiente.
Dito isso, os sistemas SGBD são realmente bons em realizar operações entre duas tabelas (como junções).Muito do tempo do otimizador é gasto nessas partes das consultas porque geralmente são mais caras.
Por exemplo, o SGBD poderia classificar isso IN
lista e, usando um índice em item_desc
, filtre os resultados muito rapidamente.Você não pode fazer essa otimização ao listar várias seleções, como no primeiro exemplo.
Quando você usa IN
, você está criando uma tabela improvisada e filtrando usando essas técnicas mais eficientes de combinação de tabelas.
EDITAR:Publiquei esta resposta antes que o OP mencionasse o DBMS específico.Acontece que NÃO é assim que o SQL Server trata essa consulta, mas pode ser válido para outros sistemas DBMS.Ver A resposta de JNK para uma resposta mais específica e precisa.