Eu estava trabalhando com uma consulta que escrevi hoje e tive que alterar o código da 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 foi executado por 15 minutos e não retornou nada, mas o seguinte me deu meu conjunto de resultados em 1,5 minutos
item_desc IN (
'item 1'
,'item 2'
,'item 3'
,'item 4'
)
Eu fiz isso no SQL e estou me perguntando por que a IN (lista de itens) teve um desempenho muito mais rápido que a instrução OR.
- EDIT - SQL Server 2008, peço desculpas por não colocar essas informações em primeiro lugar.
Aqui está a consulta na íntegra usando as OR
instruçõ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,
OR
como você faz na consulta real acima, você permite que o mecanismo entre em curto-circuito.WHERE A AND B OR C
será avaliado como verdadeiro mesmo que A e B sejam falsos, se C for verdadeiro. Se você dizWHERE A and B OR C OR D OR E OR F
como faz acima, oAND
fator pode ser considerado. A lógica equivalente real iria encapsular asOR
séries acima em parêntesis para que eles sejam tratados como um conjunto:WHERE A AND (B OR C OR D OR E)
. É assim que umIN
é tratado.AND
e tratada anteriormenteOR
, portanto, sua consulta acima é equivalente aWHERE (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, se alguma das 3 últimas condições for verdadeira, poderá causar um curto-circuito no restante da avaliação.Respostas:
A resposta de Oleski está incorreta. Para o SQL Server 2008, uma
IN
lista é refatorada para uma série deOR
instruções. Pode ser diferente no MySQL, por exemplo.Estou bastante certo de que se você gerasse planos de execução reais para ambas as suas consultas, eles seriam idênticos.
Com toda a probabilidade, a segunda consulta foi mais rápida porque você a executou em segundo lugar , e a primeira consulta já havia retirado todas as páginas de dados do banco de dados e pagado o custo de IO. A segunda consulta foi capaz de ler todos os dados da memória e executar muito mais rapidamente.
Atualizar
A fonte real da variação é provável que as consultas não sejam equivalentes . Você tem duas
OR
listas diferentes abaixo:e depois
Nas duas
WHERE
cláusulas, a precedência do operador (onde AND é tratado antes do OR) significa que a lógica real executada pelo mecanismo é:Se você substituir as
OR
listas por umaIN
expressão, a lógica será:O que é radicalmente diferente.
fonte
IN
não é equivalente aos seusOR
s acima, devido às outras condições na suaWHERE
cláusula na consulta real. Basicamente, as consultas retornarão resultados diferentes.A melhor maneira de saber é examinar o plano de consulta real usando algo parecido
EXPLAIN
. Isso deve lhe dizer exatamente o que o DBMS está fazendo e, então, você pode ter uma idéia muito melhor por que é mais eficiente.Com isso dito, os sistemas DBMS 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 DBMS pode classificar essa
IN
lista e, usando um índiceitem_desc
, filtrar os resultados muito rapidamente. Você não pode fazer essa otimização ao listar várias seleções, como no primeiro exemplo.Ao usar
IN
, você está criando uma tabela de improviso e filtrando essas técnicas de combinação de tabela mais eficientes.EDIT : Eu postei esta resposta antes do OP mencionar 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. Consulte a resposta do JNK para obter uma resposta mais específica e precisa.
fonte
IN
não seria tão rápido se fosse uma subseleção com 100 registros ou mil.IN
instrução não é convertida em uma tabela, é tratada de forma idêntica a uma série deOR
s.