SQL IN () 대 OR


23

나는 오늘 내가 쓴 쿼리로 작업 중이었고 WHERE대신 IN (목록) 필터를 사용 하도록 절 에서 코드를 변경해야했습니다.

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

위의 내용은 15 분 동안 실행되었지만 아무것도 반환하지 않았지만 다음 결과는 1.5 분 안에 결과 세트를 제공했습니다.

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

SQL 에서이 작업을 수행하고 왜 IN (항목 목록)이 OR 문보다 훨씬 빨리 수행되었는지 궁금합니다.

-편집-SQL Server 2008,이 정보를 처음에 넣지 않은 것에 대해 사과드립니다.

다음은 OR명령문을 사용한 쿼리 전체입니다 .

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

고맙습니다,


10
쿼리 계획을 보셨습니까?

1
이것은 매우 구현에 따라 다릅니다. 어떤 DBMS를 사용하고 있습니까?
제임스 앤더슨

나는 쿼리 계획을 보지 않았으며, 이것이 쿼리와 관련이 있는지 또는 사실이 문제인지 항상 알지 못했습니다.이 방식은 항상 이런 방식으로 작동합니다.
MCP_infiltrator

3
@MCP_infiltrator 따라서 논리가 동일하지 않으므로 실행 계획이 동일하지 않습니다. 사용하는 경우 OR위의 실제 쿼리에서처럼, 당신은 단락에 엔진을 할 수 있습니다. WHERE A AND B OR CC가 참이면 A AND B가 거짓이더라도 참으로 평가됩니다. WHERE A and B OR C OR D OR E OR F당신이 위에서하는 것처럼 말하면 , 그 AND 요소는 제외 될 수 있습니다. 실제 동등한 논리는 OR위 의 시리즈를 괄호로 묶어서 세트로 취급됩니다 WHERE A AND (B OR C OR D OR E). 이것이 어떻게 IN취급 되는지 입니다.
JNK

5
AND이전 OR에 처리 된 지정된 SQL Server의 연산자 우선 순위는 위의 쿼리와 동일합니다 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'. 즉, 마지막 3 가지 조건 중 하나라도 해당 되는 경우 나머지 평가를 단락시킬 수 있습니다.
JNK

답변:


28

Oleski의 답변이 잘못되었습니다. SQL Server 2008의 경우 IN목록이 일련의 OR문으로 리팩토링됩니다 . MySQL에서는 다를 수 있습니다.

두 쿼리 모두에 대한 실제 실행 계획을 생성하면 동일하다는 것을 확신합니다.

두 번째 쿼리는 두 번째 쿼리로 인해 더 빨리 실행 되었으며 첫 번째 쿼리는 이미 데이터베이스에서 모든 데이터 페이지를 가져와 IO 비용을 지불했습니다. 두 번째 쿼리는 메모리에서 모든 데이터를 읽고 훨씬 빠르게 실행할 수있었습니다.

최신 정보

분산의 실제 원인은 쿼리가 같지 않을 가능성이 있습니다. OR아래에 두 가지 다른 목록이 있습니다.

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'

그리고 나중에

 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'

WHERE절 모두 에서 연산자 우선 순위 (AND 앞에 AND가 처리되는 경우)는 엔진이 실행하는 실제 논리가 다음과 같습니다.

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

사용자가 교체 할 경우 OR으로 목록을 IN표현, 논리는 다음과 같습니다

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

근본적으로 다릅니다.


2
@MCP_infiltrator 글쎄, 그것은 가정을 만드는 데 문제가 있습니다 :) 당신은 실제로 두 가지에 대한 실제 exec 계획을 얻어야하고 차이점이 있는지 확인해야합니다.
JNK

4
고급 DB의 질문이 잘 경우에, 당신은 또한에 요청할 수 있습니다 데이터베이스 관리자 - 전체 공개, 내가 거기 중재자 해요,하지만 고급 SQL이나 SQL 최적화 문제의 경우 우리가 특히 SQL Server에 대한 전문가의 톤이
JNK를

1
방금 두 가지 실행 계획을 살펴 보았고 서로 다릅니다. OR 문이있는 쿼리는 Clustered Index Scan에서 비용의 68 %를 차지하며 IN 문은 26 %이며 실행 단계도 줄어 듭니다.
MCP_infiltrator

3
@MCP_infiltrator 필요하지 않습니다. 원본 게시물에 대한 내 의견을 맨 위에보십시오. 실제 쿼리에서 절의 다른 조건 때문에 위의 IN것과 동일하지 않습니다 . 기본적으로 쿼리는 다른 결과를 반환합니다. ORWHERE
JNK

3
@MCP_infiltrator DBA.SE에 동일한 질문을 게시 할 필요가 없습니다. JNK는 이에 대한 답변을 얻었습니다 (그리고 비슷한 답변을 얻을 수 있습니다). 의견 상자에 원하는 것을 언급하십시오. 개조는 돌봐 줄 것입니다.
ypercubeᵀᴹ

7

가장 좋은 방법은과 같은 것을 사용하여 실제 쿼리 계획을 보는 것 EXPLAIN입니다. 이를 통해 DBMS가 수행하는 작업을 정확하게 파악할 수 있으며 왜 더 효율적인지 알 수 있습니다.

따라서 DBMS 시스템은 조인과 같은 두 테이블 간의 작업을 수행하는 데 능숙합니다. 이러한 최적화 부분은 일반적으로 비용이 많이 들기 때문에 이러한 쿼리 부분에 많은 시간이 소요됩니다.

예를 들어 DBMS는 해당 IN목록을 정렬하고 on on 인덱스를 사용 item_desc하여 결과를 매우 빠르게 필터링 할 수 있습니다. 첫 번째 예에서와 같이 선택 항목을 나열 할 때 해당 최적화를 수행 할 수 없습니다.

를 사용 IN하면 즉석 테이블을 만들고보다 효율적인 테이블 결합 기술을 사용하여 필터링합니다.

편집 : OP가 특정 DBMS를 언급하기 전에이 답변을 게시했습니다. 이것은 SQL Server가이 쿼리를 처리하는 방식이 아니라 다른 DBMS 시스템에 유효 할 수 있습니다. 보다 구체적이고 정확한 답변 은 JNK의 답변 을 참조하십시오 .


카디널리티와 관련이 있다고 생각합니다. 즉 IN이 100 개 거기에 기록, 또는 천과 부속 선택이 있다면 너무 빨리하지 않을 것입니다.
Robert Harvey

@RobertHarvey 그렇습니다. 아마 사실 일 것입니다.
Oleksi

감사합니다 @Oleksi DBMS가 IN 문을 즉석 목록으로
만들지 몰랐습니다

1
-1-SQL Server에서 IN명령문은 테이블로 변환되지 않으며 일련의 ORs와 동일하게 취급 됩니다.
JNK

2
@ Katana314 EXPLAIN이 SQL Server의 키워드 (OP에서 사용중인 키워드) 인 경우 동의하지만 관련이 없습니다.
JNK
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.