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.