SQL WHERE .. IN-Klausel mehrere Spalten


173

Ich muss die folgende Abfrage in SQL Server implementieren:

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
)

Die WHERE..IN-Klausel erlaubt jedoch nur eine Spalte. Wie kann ich 2 oder mehr Spalten mit einem anderen inneren SELECT vergleichen?


Ich habe versucht, einen Überblick über die relevanten Lösungen zu geben, mit den erforderlichen Vorsichtsmaßnahmen hier: stackoverflow.com/a/54389589/983722
Dennis Jaheruddin

Antworten:


110

Sie können aus der Unterabfrage eine abgeleitete Tabelle erstellen und table1 mit dieser abgeleiteten Tabelle verknüpfen:

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
oder allgemeiner SELECT * FROM Tabelle INNER JOIN otherTable ON (table.x = otherTable.a AND table.y = otherTable.b)
ala

4
Was ist mit den mehreren Zeilen, die existieren würden, wenn Tabelle 2 ein Kind von Tabelle 1 ist? Und warum LEFT JOIN?
Gbn

1
Ja, INNER JOIN wäre hier performanter. Ein LEFT JOIN zu machen und die Nullen aus Tabelle 2 zu filtern, ist nur eine ausführliche Möglichkeit, einen INNER JOIN zu verwenden
Pstr

Falsch, dies liefert die Zeile mehrmals, vorausgesetzt, die verknüpfte Tabelle kann mehrmals verknüpft werden. Andernfalls führen Sie eine innere Verknüpfung durch, und Sie können sich das Wo ersparen.
Stefan Steiger

123

Sie sollten stattdessen die WHERE EXISTS-Syntax verwenden.

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
Dies würde zwar funktionieren, konvertiert jedoch die nicht korrelierte Abfrage in der Frage in eine korrelierte Abfrage. Wenn das Abfrageoptimierungsprogramm nicht clever ist, erhalten Sie möglicherweise eine O (n ^ 2) -Leistung :-(. Aber vielleicht unterschätze ich das Optimierungsprogramm ...
sleske

1
Ich benutze solche Syntaxen die ganze Zeit ohne Probleme. Sofern Sie kein älteres Optimierungsprogramm (6.5, 7, 8 usw.) verwenden, sollte es kein Problem mit dieser Syntax geben.
Mrdenny

1
@sleske: EXISTS ist bei weitem besser: siehe meine Kommentare in meiner Antwort. Und testen Sie es zuerst. @mrdenny: Ich habe Ihre Antwort zuerst falsch verstanden, ich würde auch EXISTS verwenden
gbn

6
Dies ist am effizientesten, +1. Siehe diesen Artikel in meinem Blog für Leistungsvergleich: explainextended.com/2009/06/17/efficient-exists
Quassnoi

1
Sogar SQL 2000 könnte die meisten korrelierten Unterabfragen verarbeiten, ohne die Abfrage in ein O (n ^ 2) umzuwandeln. Könnte schon bei 6.5 ein Problem gewesen sein.
GilaMonster

14

WARNUNG ZU LÖSUNGEN:

VIELE BESTEHENDE LÖSUNGEN GEBEN DIE FALSCHE AUSGABE, WENN REIHEN NICHT EINZIGARTIG SIND

Wenn Sie die einzige Person sind, die Tabellen erstellt, ist dies möglicherweise nicht relevant. Mehrere Lösungen geben jedoch eine andere Anzahl von Ausgabezeilen als der betreffende Code an, wenn eine der Tabellen möglicherweise keine eindeutigen Zeilen enthält.

WARNUNG ÜBER DIE PROBLEMSTELLUNG:

IN MIT MEHREREN SPALTEN GIBT ES NICHT, DENKEN SIE SORGFÄLTIG, WAS SIE WOLLEN

Wenn ich ein In mit zwei Spalten sehe, kann ich mir vorstellen, dass es zwei Dinge bedeutet:

  1. Der Wert von Spalte a und Spalte b wird unabhängig voneinander in der anderen Tabelle angezeigt
  2. Die Werte von Spalte a und Spalte b werden in der anderen Tabelle zusammen in derselben Zeile angezeigt

Szenario 1 ist ziemlich trivial. Verwenden Sie einfach zwei IN-Anweisungen.

In Übereinstimmung mit den meisten vorhandenen Antworten gebe ich hiermit einen Überblick über die genannten und zusätzlichen Ansätze für Szenario 2 (und eine kurze Beurteilung):

EXISTS (sicher, empfohlen für SQL Server)

Wie von @mrdenny bereitgestellt, klingt EXISTS genau so, wie Sie es suchen. Hier ist sein Beispiel:

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

LEFT SEMI JOIN (Sicher, empfohlen für Dialekte, die dies unterstützen)

Dies ist eine sehr prägnante Art des Beitritts, aber leider unterstützen die meisten SQL-Dialekte, einschließlich SQL Server, dies derzeit nicht.

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

Mehrere IN-Anweisungen (sicher, aber Vorsicht vor Codeduplizierungen)

Wie von @cataclysm erwähnt, kann die Verwendung von zwei IN-Anweisungen ebenfalls den Trick ausführen. Vielleicht übertrifft es sogar die anderen Lösungen. Was Sie jedoch sehr vorsichtig sein sollten, ist die Codeduplizierung. Wenn Sie jemals aus einer anderen Tabelle auswählen oder die where-Anweisung ändern möchten, besteht ein erhöhtes Risiko, dass Sie Inkonsistenzen in Ihrer Logik erzeugen.

Grundlösung

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

Lösung ohne Codeduplizierung (Ich glaube, dies funktioniert nicht in regulären SQL Server-Abfragen)

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 (technisch kann es sicher gemacht werden, aber oft wird dies nicht getan)

Der Grund, warum ich die Verwendung eines inneren Joins als Filter nicht empfehle, liegt darin, dass in der Praxis häufig Duplikate in der rechten Tabelle Duplikate in der linken Tabelle verursachen. Und um die Sache noch schlimmer zu machen, wird das Endergebnis manchmal deutlich, während die linke Tabelle möglicherweise nicht eindeutig sein muss (oder in den ausgewählten Spalten nicht eindeutig sein muss). Darüber hinaus haben Sie die Möglichkeit, eine Spalte auszuwählen, die in der linken Tabelle nicht vorhanden ist.

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

Häufigste Fehler:

  1. Direkter Beitritt zu T2 ohne sichere Unterabfrage. Dies führt zu einem Risiko der Vervielfältigung.
  2. SELECT * (Garantiert, um Spalten von T2 zu erhalten)
  3. SELECT c (Garantiert nicht, dass Ihre Spalte von T1 kommt und immer kommt)
  4. Kein DISTINCT oder DISTINCT am falschen Ort

KONKATENATION VON SPALTEN MIT SEPARATOR (Nicht sehr sichere, schreckliche Leistung)

Das Funktionsproblem besteht darin, dass es schwierig wird, sicherzustellen, dass das Ergebnis 100% genau ist, wenn Sie ein Trennzeichen verwenden, das in einer Spalte auftreten kann. Das technische Problem besteht darin, dass diese Methode häufig Typkonvertierungen durchführt und Indizes vollständig ignoriert, was zu einer möglicherweise schrecklichen Leistung führt. Trotz dieser Probleme muss ich zugeben, dass ich es manchmal immer noch für Ad-hoc-Abfragen in kleinen Datensätzen verwende.

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

Wenn Ihre Spalten numerisch sind, müssen Sie sie bei einigen SQL-Dialekten zuerst in Zeichenfolgen umwandeln. Ich glaube, SQL Server wird dies automatisch tun.


Zum Abschluss: Wie üblich gibt es in SQL viele Möglichkeiten, dies zu tun. Wenn Sie sichere Entscheidungen treffen, vermeiden Sie Überraschungen und sparen auf lange Sicht Zeit und Kopfzerbrechen.


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

Hinweis:
Oracle ignoriert Zeilen, in denen eine oder mehrere der ausgewählten Spalten NULL sind. In diesen Fällen möchten Sie wahrscheinlich die NVL- Funktion verwenden, um NULL einem speziellen Wert zuzuordnen (der nicht in den Werten enthalten sein sollte).

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

2
postgres unterstützt, where (colA,colB) in (... some list of tuples...)aber ich bin mir nicht sicher, welche anderen Datenbanken dasselbe tun. Es würde mich interessieren zu wissen.
Max Murphy

2
Diese Syntax wird auch in Oracle und DB2 / 400 unterstützt (wahrscheinlich auch in DB2). Ich wünschte, SQL Server würde es unterstützen.
CrazyIvan1974

DB2 unterstützt dies.
Telmo Marques

Sogar SQLite unterstützt es.
Holger Jakobs

13

Eine einfache EXISTS-Klausel ist am saubersten

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
)

Wenn Sie mehrere Zeilen in der Korrelation haben, gibt ein JOIN mehrere Zeilen in der Ausgabe an, sodass Sie unterschiedliche Zeilen benötigen. Das macht die EXISTS normalerweise effizienter.

Hinweis SELECT *mit einem JOIN würde auch Spalten aus den Zeilenbegrenzungstabellen enthalten


2

Warum WHERE EXISTS oder DERIVED TABLES verwenden, wenn Sie nur eine normale innere Verknüpfung durchführen können:

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

Wenn das Paar (CM_PLAN_ID, Individual_ID) in der Statustabelle nicht eindeutig ist, benötigen Sie möglicherweise stattdessen ein SELECT DISTINCT t. *.


3
Und das DISTINCT bedeutet normalerweise, dass ein EXISTS effizienter ist
gbn

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

1.Verwenden mit EXISTS[Durchschnittliche Abfragezeit: 1,42 s]

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.Verwenden mit zwei Zeilen INKlausel [Durchschnittliche Abfragezeit: 0,37 s]

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.Verwenden mit INNNER JOINMuster [Durchschnittliche Abfragezeit: 2,9 s]

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

Also entschied ich mich für die zweite Option.


Warnung für zukünftige Leser: Entsprechend der Frage möchten Sie wahrscheinlich ANDeher ORAnweisungen als Anweisungen verwenden.
Dennis Jaheruddin

@ TennisJaheruddin .. Vielen Dank für Ihren Kommentar und sehr schöne detaillierte Erklärungen Ihrer Antwort. Sie haben Recht, ORAussage führt wahrscheinlich zu Überschneidungen. In meinem Fall gibt es keine Zeilen, die dieselbe src_idund dest_ideine einzelne Zeile enthalten. In meinem Fall kommt es also nicht zu Duplikaten.
Katastrophe


-2

Wenn Sie für eine Tabelle möchten, verwenden Sie die folgende Abfrage

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

und Tabellendaten wie folgt

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

Dann wie folgt ausgeben

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

id in (1,2) and studentName in ('a','b')ist total nicht das gleiche wie (id, studentName) in ((1,'a'),(2,'b')). Stellen Sie sich einen Datensatz mit id = 2 und name = 'a' vor. Wenn die ID eindeutig ist, wird der Effekt natürlich verringert, aber wenn die ID eindeutig ist, müssen wir überhaupt nicht nach Namen filtern.
Quetzalcoatl

-2

Wir können das einfach machen.

   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

Das Verketten der Spalten in irgendeiner Form ist ein "Hack", aber wenn das Produkt keine Semi-Joins für mehr als eine Spalte unterstützt, haben Sie manchmal keine Wahl.

Beispiel dafür, wo eine innere / äußere Verbindungslösung nicht funktionieren würde:

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

Wenn die Abfragen nicht trivialer Natur sind, haben Sie manchmal keinen Zugriff auf die Basistabelle, um regelmäßige innere / äußere Verknüpfungen durchzuführen.

Wenn Sie diesen "Hack" verwenden, müssen Sie beim Kombinieren von Feldern genügend Trennzeichen dazwischen einfügen, um Fehlinterpretationen zu vermeiden, z ColA + ":-:" + ColB


Diese Antwort scheint inkonsistent zu sein (erwähnt die Verkettung und liefert dann ein anderes Beispiel). Leichter gesagt: Wir haben immer eine Wahl ;-) Ich habe das Verkettungsbeispiel hier zu meiner Übersicht hinzugefügt
Dennis Jaheruddin

-3

Ich habe auf diese Weise leichter gegründet

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 
) 

Ich hoffe das hilft :)


9
Autsch, hier wird kein Index für den String concat verwendet.
Mrdenny

9
Ich habe dies abgelehnt, da es einfach gefährlich ist! Wenn CM_PLAN_ID = 45und Individual_ID = 3dann Verkettung führt 453- was nicht von dem Fall zu unterscheiden ist, wo CM_PLAN_ID = 4und Individual_ID = 53... um Ärger zu bitten, hätte ich gedacht
El Ronnoco

5
..natürlich könnten Sie mit einem beliebigen speziellen char zB verketten 45_3oder 45:3aber es ist noch nicht eine schöne Lösung und natürlich als @mrdenny sagt Indizes wird nicht jetzt genutzt werden , dass eine Transformation statt auf den Säulen genommen.
El Ronnoco

1
Ich habe dies auch abgelehnt, da diese Lösung wirklich nur ein schneller "Hack" ist. Es ist langsam und wie El Ronnoco sagte, kann es zu Fehlern führen.

-4

Ein einfacher und falscher Weg wäre, zwei Spalten mit + zu kombinieren oder zu verketten und eine Spalte zu erstellen.

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

Das wäre natürlich ziemlich langsam. Kann nicht in der Programmierung verwendet werden, aber wenn Sie nur nach einer Überprüfung fragen, kann etwas verwendet werden.


10
In der Tat, und es kann zu Fehlern führen, da zB 'ab' + 'c' = 'a' + 'bc'
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.