SQL WHERE .. IN 절 여러 열


173

SQL Server에서 다음 쿼리를 구현해야합니다.

select *
from table1
WHERE  (CM_PLAN_ID,Individual_ID)
IN
(
 Select CM_PLAN_ID, Individual_ID
 From CRM_VCM_CURRENT_LEAD_STATUS
 Where Lead_Key = :_Lead_Key
)

그러나 WHERE..IN 절은 하나의 열만 허용합니다. 두 개 이상의 열을 다른 내부 SELECT와 어떻게 비교할 수 있습니까?


나는 여기에 neccesary주의와 함께, 관련 솔루션의 개요를 제공하기 위해 노력 : stackoverflow.com/a/54389589/983722
데니스 Jaheruddin

답변:


110

하위 쿼리에서 파생 테이블을 만들고 table1을이 파생 테이블에 조인 할 수 있습니다.

select * from table1 LEFT JOIN 
(
   Select CM_PLAN_ID, Individual_ID
   From CRM_VCM_CURRENT_LEAD_STATUS
   Where Lead_Key = :_Lead_Key
) table2
ON 
   table1.CM_PLAN_ID=table2.CM_PLAN_ID
   AND table1.Individual=table2.Individual
WHERE table2.CM_PLAN_ID IS NOT NULL

7
더 일반적으로 SELECT * FROM 테이블 INNER JOIN otherTable ON (table.x = otherTable.a AND table.y = otherTable.b)
ala

4
테이블 2가 테이블 1의 하위 인 경우 존재할 여러 행은 어떻습니까? 왜 왼쪽이 합류합니까?
gbn

1
예, INNER JOIN이 더 성능이 좋을 것입니다. LEFT JOIN을 수행하고 표 2에서 널을 필터링하는 것은 INNER JOIN을 사용하는
간단한

잘못, 이것은 조인 테이블을 여러 번 조인 할 수 있다고 가정하면 행을 여러 번 전달합니다 ... 그렇지 않으면 내부 조인을 수행하면 위치를 절약 할 수 있습니다.
Stefan Steiger

123

대신 WHERE EXISTS 구문을 사용하고 싶을 것입니다.

SELECT *
FROM table1
WHERE EXISTS (SELECT *
              FROM table2
              WHERE Lead_Key = @Lead_Key
                        AND table1.CM_PLAN_ID = table2.CM_PLAN_ID
                        AND table1.Individual_ID = table2.Individual_ID)

5
이것은 작동하지만 질문의 상관되지 않은 쿼리를 상관 된 쿼리로 변환합니다. 쿼리 최적화 프로그램이 똑똑하지 않는 한, ...이 당신에게 O (N ^ 2) 성능을 :-( 줄 수도 있지만 어쩌면 내가 최적화를 과소 평가하고 있습니다.
sleske

1
나는 이런 식으로 항상 문제없이 사용합니다. 구형 옵티 마이저 (6.5, 7, 8 등)를 사용하지 않는 한이 구문에는 문제가 없습니다.
mrdenny

1
@ sleske : EXISTS가 훨씬 좋습니다 : 내 답변에서 내 의견을보십시오. 먼저 테스트하십시오. @mrdenny : 나는 처음에 당신의 대답을 잘못 읽고, EXISTS도 사용합니다
gbn

6
가장 효율적인 +1입니다. 성능 비교에 대한 내 블로그에서이 문서를 참조하십시오 explainextended.com/2009/06/17/efficient-exists
Quassnoi

1
SQL 2000조차도 쿼리를 O (n ^ 2)로 바꾸지 않고도 대부분의 상관 된 하위 쿼리를 처리 할 수 ​​있습니다. 6.5에서 다시 문제가 발생했을 수 있습니다.
GilaMonster

14

솔루션에 대한 경고 :

로크가 고유하지 않으면 많은 기존 솔루션이 잘못된 출력을 제공합니다.

테이블을 생성하는 유일한 사람이라면 관련이 없을 수 있지만 테이블 중 하나에 고유 한 행이 포함되어 있지 않을 때 여러 솔루션에서 해당 코드와 다른 수의 출력 행을 제공합니다.

문제 진술에 대한 경고 :

여러 개의 열이 포함되어 있으면 원하는 것을 신중하게 생각하십시오.

두 개의 열이있는 in을 볼 때 두 가지를 의미한다고 상상할 수 있습니다.

  1. 열 a와 열 b의 값은 다른 테이블에 독립적으로 나타납니다.
  2. 열 a와 열 b의 값이 같은 행에 다른 테이블에 함께 나타납니다.

시나리오 1은 매우 사소합니다. 간단히 두 개의 IN 문을 사용하십시오.

대부분의 기존 답변에 따라 시나리오 2 (및 간단한 판단)에 대해 언급되고 추가 된 접근 방식에 대한 개요를 제공합니다.

존재 (안전, SQL Server에 권장)

@mrdenny가 제공 한 EXISTS는 당신이 찾고있는 것과 똑같이 들립니다. 그의 예는 다음과 같습니다.

SELECT * FROM T1
WHERE EXISTS
(SELECT * FROM T2 
 WHERE T1.a=T2.a and T1.b=T2.b)

LEFT SEMI JOIN (안전, 지원하는 방언에 권장)

이것은 매우 간결한 방법이지만, 불행히도 SQL 서버를 포함한 대부분의 SQL 방언은이를 지원하지 않습니다.

SELECT * FROM T1
LEFT SEMI JOIN T2 ON T1.a=T2.a and T1.b=T2.b

여러 IN 문 (안전하지만 코드 중복에주의)

@cataclysm이 언급 한 것처럼 두 개의 IN 문을 사용하면 트릭을 수행 할 수 있으며 아마도 다른 솔루션보다 성능이 뛰어납니다. 그러나 매우 조심해야 할 것은 코드 복제입니다. 다른 테이블에서 선택하거나 where 문을 변경하려는 경우 논리에 불일치가 발생할 위험이 커집니다.

기본 솔루션

SELECT * from T1
WHERE a IN (SELECT a FROM T2 WHERE something)
AND b IN (SELECT b FROM T2 WHERE something)

코드 복제가없는 솔루션 (정기적 인 SQL Server 쿼리에서는 작동하지 않는다고 생각합니다)

WITH mytmp AS (SELECT a, b FROM T2 WHERE something);
SELECT * from T1 
WHERE a IN (SELECT a FROM mytmp)
AND b IN (SELECT b FROM mytmp)

INNER JOIN (기술적으로는 안전 할 수 있지만 종종 수행되지 않음)

내부 조인을 필터로 사용하지 않는 이유는 실제로 사람들이 종종 오른쪽 테이블의 중복으로 인해 왼쪽 테이블에서 중복이 발생하기 때문입니다. 그리고 문제를 악화시키기 위해 때로는 왼쪽 테이블이 실제로 고유하지 않아도되는 (또는 선택한 열에서 고유하지 않아도 됨) 최종 결과가 명확 해집니다. 또한 왼쪽 테이블에 존재하지 않는 열을 실제로 선택할 수있는 기회를 제공합니다.

SELECT T1.* FROM T1
INNER JOIN 
(SELECT DISTINCT a, b FROM T2) AS T2sub
ON T1.a=T2sub.a AND T1.b=T2sub.b

가장 일반적인 실수 :

  1. 안전한 하위 쿼리없이 T2에서 직접 결합 중복의 위험이 있음)
  2. SELECT * (T2에서 열을 가져 오기 위해 보장됨)
  3. SELECT c (열이 항상오고 T1에서 오는 것을 보장하지는 않습니다)
  4. 잘못된 장소에 DISTINCT 또는 DISTINCT가 없습니다.

분리기와 열의 연결 (매우 안전하고 끔찍한 성능은 아님)

기능상의 문제는 열에서 발생할 수있는 구분 기호를 사용하면 결과가 100 % 정확하다는 것을 까다롭게한다는 것입니다. 기술적 인 문제는이 방법으로 인해 유형 변환이 자주 발생하고 인덱스를 완전히 무시하여 성능이 끔찍할 수 있다는 것입니다. 이러한 문제에도 불구하고, 때때로 작은 데이터 세트의 임시 쿼리에 여전히 사용한다는 점을 인정해야합니다.

SELECT * FROM T1
WHERE CONCAT(a,"_",b) IN 
(SELECT CONCAT(a,"_",b) FROM T2)

열이 숫자 인 경우 일부 SQL 언어에서는 먼저 문자열로 캐스트해야합니다. SQL 서버가 자동 으로이 작업을 수행 할 것이라고 생각합니다.


정리 : 평소와 같이 SQL에는 여러 가지 방법이 있으며, 안전한 선택을 사용하면 예상치 못한 결과를 피하고 장기적으로 시간과 헤드를 절약 할 수 있습니다.


13
select * from tab1 where (col1,col2) in (select col1,col2 from tab2)

참고 :
Oracle은 선택된 하나 이상의 열이 NULL 인 행을 무시합니다. 이 경우 NVL -Funktion을 사용하여 NULL을 특수 값 (값에 포함되지 않아야 함)에 맵핑하려고합니다.

select * from tab1
where (col1, NVL(col2, '---') in (select col1, NVL(col2, '---') from tab2)

2
postgres는 지원 where (colA,colB) in (... some list of tuples...)하지만 다른 데이터베이스가 어떻게 작동하는지 잘 모르겠습니다. 알고 싶습니다.
Max Murphy

2
이 구문은 Oracle 및 DB2 / 400에서도 지원됩니다 (아마도 DB2). Wish SQL Server가 지원했습니다.
CrazyIvan1974

DB2가이를 지원합니다.
Telmo Marques

SQLite조차도 그것을 지원합니다.
Holger Jakobs

13

간단한 EXISTS 절이 가장 깨끗합니다.

select *
from table1 t1
WHERE
EXISTS
(
 Select * --or 1. No difference...
 From CRM_VCM_CURRENT_LEAD_STATUS Ex
 Where Lead_Key = :_Lead_Key
-- correlation here...
AND
t1.CM_PLAN_ID = Ex.CM_PLAN_ID AND t1.CM_PLAN_ID =  Ex.Individual_ID
)

상관 관계에 여러 행이있는 경우 JOIN은 출력에 여러 행을 제공하므로 고유해야합니다. 일반적으로 EXISTS가 더 효율적입니다.

참고 SELECT *A를 또한 행 제한 테이블에서 열을 포함 할 것이다 가입


2

정상적인 내부 조인을 수행 할 수있는 경우 WHERE EXISTS 또는 DERIVED TABLES를 사용하는 이유 :

SELECT t.*
FROM table1 t
INNER JOIN CRM_VCM_CURRENT_LEAD_STATUS s
    ON t.CM_PLAN_ID = s.CM_PLAN_ID
    AND t.Individual_ID = s.Individual_ID
WHERE s.Lead_Key = :_Lead_Key

(CM_PLAN_ID, Individual_ID) 쌍이 상태 테이블에서 고유하지 않으면 대신 SELECT DISTINCT t. *가 필요할 수 있습니다.


3
그리고 DISTINCT는 일반적으로 EXISTS가 더 효율적임을 의미합니다
gbn

0
Postgres SQL  : version 9.6
Total records on tables : mjr_agent = 145, mjr_transaction_item = 91800

1. EXISTS[평균 쿼리 시간 : 1.42s] 와 함께 사용

SELECT count(txi.id) 
FROM 
mjr_transaction_item txi
WHERE 
EXISTS ( SELECT 1 FROM mjr_agent agnt WHERE agnt.agent_group = 0 AND (txi.src_id = agnt.code OR txi.dest_id = agnt.code) ) 

IN2.2 줄로 사용 절 [평균 쿼리 시간 : 0.37 초]

SELECT count(txi.id) FROM mjr_transaction_item txi
WHERE 
txi.src_id IN ( SELECT agnt.code FROM mjr_agent agnt WHERE agnt.agent_group = 0 ) 
OR txi.dest_id IN ( SELECT agnt.code FROM mjr_agent agnt WHERE agnt.agent_group = 0 )

INNNER JOIN3. 패턴 과 함께 사용 [평균 쿼리 시간 : 2.9 초]

SELECT count(DISTINCT(txi.id)) FROM mjr_transaction_item txi
INNER JOIN mjr_agent agnt ON agnt.code = txi.src_id OR agnt.code = txi.dest_id
WHERE 
agnt.agent_group = 0

그래서 두 번째 옵션을 선택했습니다.


미래 독자들을위한 경고 : 질문에 따라, 당신은 아마도 AND진술보다는 진술 을 사용하고 싶을 것입니다 OR.
Dennis Jaheruddin

@DennisJaheruddin .. 귀하의 의견과 답변에 대한 매우 자세한 설명에 감사드립니다. 당신은 옳습니다. OR진술은 아마도 중복을 일으킬 것입니다. 내 경우에는 동일 src_id하고 dest_id단일 행 을 포함하는 행이 없습니다 . 따라서 내 경우에는 중복이 발생하지 않습니다.
대격변


-2

하나의 테이블을 원한다면 다음 쿼리를 사용하십시오.

SELECT S.* 
FROM Student_info S
  INNER JOIN Student_info UT
    ON S.id = UT.id
    AND S.studentName = UT.studentName
where S.id in (1,2) and S.studentName in ('a','b')

다음과 같은 테이블 데이터

id|name|adde|city
1   a   ad  ca
2   b   bd  bd
3   a   ad  ad
4   b   bd  bd
5   c   cd  cd

다음과 같이 출력

id|name|adde|city
1   a   ad  ca
2   b   bd  bd

id in (1,2) and studentName in ('a','b')와 완전히 동일하지 않습니다 (id, studentName) in ((1,'a'),(2,'b')). id가 2이고 name이 'a'인 레코드를 생각해보십시오. 물론 ID가 고유하면 효과가 줄어들지 만 ID가 고유하면 이름을 전혀 필터링하지 않아도됩니다.
quetzalcoatl

-2

간단히 할 수 있습니다.

   select *
   from 
    table1 t, CRM_VCM_CURRENT_LEAD_STATUS c
    WHERE  t.CM_PLAN_ID = c.CRM_VCM_CURRENT_LEAD_STATUS
    and t.Individual_ID = c.Individual_ID

-2

열을 어떤 형태로 함께 연결하는 것은 "해킹"이지만, 제품이 두 개 이상의 열에 대해 반 조인을 지원하지 않는 경우 때로는 선택의 여지가 없습니다.

내부 / 외부 조인 솔루션이 작동하지 않는 예 :

select * from T1 
 where <boolean expression>
   and (<boolean expression> OR (ColA, ColB) in (select A, B ...))
   and <boolean expression>
   ...

쿼리가 본질적으로 사소하지 않은 경우 정기적 인 내부 / 외부 조인을 수행하기 위해 기본 테이블 세트에 액세스 할 수없는 경우가 있습니다.

이 "해킹"을 사용하는 경우 필드를 결합 할 때 필드 사이에 구분 기호를 충분히 추가하여 오해를 피하십시오. ColA + ":-:" + ColB


이 답변은 일치하지 않는 것 같습니다 (멘션 연결 후 다른 예를 제공함). 또한, 더 가벼운 메모 : 우리는 항상 선택의 여지가 있습니다 ;-) 관련 주석과 함께 여기에 개요에 연결 예제를 추가했습니다 : stackoverflow.com/a/54389589/983722
Dennis Jaheruddin

-3

이 방법으로 쉽게 설립

Select * 
from table1 
WHERE  (convert(VARCHAR,CM_PLAN_ID) + convert(VARCHAR,Individual_ID)) 
IN 
(
 Select convert(VARCHAR,CM_PLAN_ID) + convert(VARCHAR,Individual_ID)
 From CRM_VCM_CURRENT_LEAD_STATUS 
 Where Lead_Key = :_Lead_Key 
) 

이 도움을 바랍니다 :)


9
여기에 문자열 연결에 인덱스를 사용하지 않습니다.
mrdenny

9
나는 그것이 명백한 위험하기 때문에 이것을 투표했다! 만약에 CM_PLAN_ID = 45그리고 Individual_ID = 3연결로 결과가 나왔다면 453– 그것은 어디에서나 ... 문제를 요구 하는 경우 CM_PLAN_ID = 4와 구별 할 수없는 Individual_ID = 53것입니다
El Ronnoco

5
..of 물론 당신은 임의의 특수 문자의 예와 연결할 수있는 45_345:3이지만 여전히 아니다 좋은 @mrdenny 인덱스가 이제 변환이 열을 발생했음을 사용되지 않습니다 말한대로 솔루션 물론.
El Ronnoco

1
이 솔루션은 정말 빠른 "해킹"일 뿐이므로이 투표에 투표했습니다. 느리고 El Ronnoco가 말했듯이 버그로 이어질 수 있습니다.

-4

간단하고 잘못된 방법은 +를 사용하여 두 열을 결합하거나 하나의 열을 연결하는 것입니다.

Select *
from XX
where col1+col2 in (Select col1+col2 from YY)

이것은 꽤 느린 코스입니다. 프로그래밍에 사용할 수 없지만 경우에 따라 확인을 위해 쿼리하는 경우 사용할 수 있습니다.


10
실제로, 그것은, 오류가 발생할 수 있습니다 예를 들어, 'AB'+ 'C'= 'A'+ 'BC'이후
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.