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句では1つの列しか使用できません。2つ以上の列を別の内部SELECTと比較するにはどうすればよいですか?


ここで必要な注意を
払って

回答:


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
またはより一般的には、INNER otherTable ONを結合テーブルSELECT * FROM(table.x = otherTable.a AND table.y = otherTable.b)
ALA

4
テーブル2がテーブル1の子である場合に存在する複数の行はどうですか?そして、なぜLEFT JOIN?
gbn 2009

1
ええ、INNER JOINの方がパフォーマンスが良くなります。LEFT JOINを実行し、テーブル2からnullをフィルター処理することは、INNER JOINを使用するための冗長な方法です
Pstr

間違っています。これは、結合されたテーブルを複数回結合できると想定して、行を複数回配信します。それ以外の場合は、内部結合を実行すれば、場所を節約できます。
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、2009

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 2009

14

ソリューションに関する警告:

行が一意でない場合、多くの既存のソリューションは誤った出力を提供します

あなたがテーブルを作成する唯一の人である場合、これは関係がないかもしれませんが、テーブルの1つが一意の行を含まない可能性がある場合、いくつかの解決策は問題のコードから異なる数の出力行を与えます。

問題ステートメントに関する警告:

複数の列が存在しない場合、何が欲しいかを慎重に考えてください

2つの列のあるインを見ると、2つの意味があると想像できます。

  1. 列aと列bの値は、他のテーブルに個別に表示されます
  2. 列aと列bの値は、同じ行の別のテーブルに一緒に表示されます

シナリオ1はかなり簡単で、2つのINステートメントを使用するだけです。

ほとんどの既存の回答に沿って、シナリオ2の前述のアプローチと追加のアプローチの概要(および簡単な判断)をここに示します。

EXISTS(安全、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で言及されているように、2つの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は、選択された1つ以上の列が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も)。SQL Serverがそれをサポートしてほしい。
CrazyIvan1974 2016年

DB2はこれをサポートしています。
Telmo Marques 2017年

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 *JOINを使用すると、行制限テーブルの列も含まれることに注意してください。


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.42秒]での使用

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) ) 

2. 2行IN句での使用[平均クエリ時間: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 )

3. INNNER JOINパターンでの使用[平均クエリ時間: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

だから、私は2番目のオプションを選びました。


将来の読者への警告:質問に沿って、おそらくANDステートメントではなくステートメントを使用したいと思うでしょうOR
Dennis Jaheruddin

@DennisJaheruddin ..あなたのコメントとあなたの答えの非常に素晴らしい説明をありがとう。あなたは正しいです、ORステートメントはおそらく重複を引き起こします。私の場合は、同じ含まれている任意の行が存在していないsrc_iddest_id一列には。したがって、私の場合、重複は発生しません。


-2

1つのテーブルが必要な場合は、次のクエリを使用します

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が一意の場合、名前をフィルタリングする必要はまったくありません。
ケツァルコアトル2018年

-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
デニス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、2011

9
これは非常に危険なので、私はこれに反対票を投じました!もしCM_PLAN_ID = 45およびIndividual_ID = 3その後、連結業績で453-場合と区別がつかないCM_PLAN_ID = 4Individual_ID = 53...私は思ってもみなかったトラブルを求めて
エルRonnoco

5
..もちろん、たとえば45_3orを任意の特殊文字と連結することもできますが、45:3それでも良い解決策ではありません。もちろん、@ mrdennyが言うように、列で変換が行われたため、インデックスは使用されません。
El Ronnoco 2013

1
このソリューションは実際には迅速な「ハック」のみであるため、私もこれに反対票を投じました。それは遅く、El Ronnocoが言ったように、それはバグを引き起こす可能性があります。

-4

単純で間違った方法は、+または連結を使用して2つの列を結合し、1つの列を作成することです。

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.