2009-07-16 6 views
118

J'ai besoin de mettre en œuvre la requête suivante dans SQL Server:SQL WHERE .. plusieurs colonnes à l'article

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 
) 

Mais la clause WHERE..IN permet seulement 1 colonne. Comment puis-je comparer 2 colonnes ou plus avec une autre SELECT interne?

Répondre

81

Vous pouvez faire une table dérivée de la sous-requête et rejoindre table1 à cette table dérivée:

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 
+5

ou plus généralement SELECT * A partir de la table INNER JOIN otherTable ON (table.x = otherTable.a AND table.y = otherTable.b) – ala

+3

Qu'en est-il des lignes multiples qui existeraient si la table 2 était un enfant de la table 1? Et pourquoi LEFT JOIN? – gbn

+0

@gbn: Merci, vous avez raison. Corrigé cela. – sleske

95

Vous devez utiliser la syntaxe WHERE EXISTS à la place.

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

Bien que cela fonctionne, il convertit la requête non corrélée dans la question en une requête corrélée. À moins que l'optimiseur de requête ne soit intelligent, cela pourrait vous donner des performances O (n^2) :-(Mais peut-être que je sous-estime l'optimiseur ... – sleske

+1

J'utilise des syntaxes comme ça tout le temps sans problème. ancien optimiseur (6.5, 7, 8, etc), il ne devrait pas avoir un problème avec cette syntaxe – mrdenny

+1

@sleske: EXISTS est de loin mieux: voir mes commentaires dans ma réponse.Et tester d'abord, @mrdenny: J'ai mal lu votre réponse au début, j'utiliserais aussi EXISTS – gbn

12

Une simple clause EXISTS est plus propre

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 
) 

Si vous avez plusieurs lignes dans la corrélation, un JOIN donne plusieurs lignes dans la sortie, donc vous aurez besoin de distinct. Ce qui rend généralement les EXISTS plus efficaces.

Note « SELECT » * avec un JOIN comprendrait également des colonnes de la ligne limitant les tables

3

Pourquoi utiliser OÙ EXISTE ou de tables sous quand vous pouvez juste faire une jointure interne normale:

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 

Si la paire de (CM_PLAN_ID, Individual_ID) n'est pas unique dans la table d'état, vous pourriez avoir besoin d'un SELECT DISTINCT t. * à la place.

+2

Et le DISTINCT signifie généralement un EXISTS est plus efficace – gbn

-3

Je fonde plus facile de cette façon

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 
) 

Hope this aide :)

+9

Ouch, pas d'utilisation de l'index ici faire à la chaîne de concat. – mrdenny

+9

J'ai voté cela parce que c'est tout simplement dangereux! Si 'CM_PLAN_ID = 45' et' Individual_ID = 3' alors la concaténation donne '453' - ce qui est indiscernable du cas où' CM_PLAN_ID = 4' et 'Individual_ID = 53' ... demandant des problèmes j'aurais pensé –

+5

. Bien sûr, vous pouvez concaténer avec un caractère spécial arbitraire, par exemple '45_3' ou' 45: 3' mais ce n'est toujours pas une bonne solution et bien sûr, comme @mrdenny dit que les index ne seront pas utilisés maintenant qu'une transformation a eu lieu Les colonnes. –

-3

façon simple et le mal serait combiner deux colonnes en utilisant + ou concaténer et faire une colonne.

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

Ce serait hors sujet plutôt lent. Ne peut pas être utilisé dans la programmation, mais si vous êtes juste en train d'interroger pour vérifier que quelque chose peut être utilisé.

+9

En effet, et cela peut conduire à des erreurs, puisque par ex. 'ab' + 'c' = 'a' + 'bc' –

1

Si vous voulez pour une table puis utilisez requête suivante

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

et les données de table comme suivre

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

Puis sortie comme suit

id|name|adde|city 
1 a ad ca 
2 b bd bd 
3
select * from tab1 where (col1,col2) in (select col1,col2 from tab2) 

Note:
Oracle ignore les lignes où une ou plusieurs des colonnes sélectionnées est NULL. Dans ces cas, vous voulez probablement utiliser la fonction NVL pour mapper NULL à une valeur spéciale (qui ne devrait pas figurer dans les valeurs);

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

postgres supporte 'where (colA, colB) dans (... une liste de tuples ...)' mais je ne suis pas sûr de ce que font les autres bases de même. Je serais intéressé de savoir. –

+2

Cette syntaxe est également supportée dans Oracle et DB2/400 (probablement aussi DB2). Je souhaite que SQL Server le supporte. – CrazyIvan1974

+0

DB2 le supporte. –

0

Nous pouvons simplement le faire.

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 
Questions connexes