SQL WHERE .. IN子句多列

ala*_*ala 161 sql-server

我需要在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
)
Run Code Online (Sandbox Code Playgroud)

但WHERE..IN子句只允许1列.如何将2个或更多列与另一个内部SELECT进行比较?

mrd*_*nny 118

您将要使用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)
Run Code Online (Sandbox Code Playgroud)

  • 这是最有效的,+ 1.请参阅我的博客中的文章以进行性能比较:http://explainextended.com/2009/06/17/efficient-exists/ (6认同)
  • 虽然这可行,但它会将问题中的不相关查询转换为相关查询.除非查询优化器很聪明,否则这可能会给你O(n ^ 2)性能:-(但也许我低估了优化器... (5认同)
  • 我一直使用这样的语法,没有任何问题。除非您使用较旧的优化器(6.5、7、8 等),否则此语法应该不会有问题。 (3认同)
  • 即使 SQL 2000 也可以处理大多数相关子查询,而无需将查询变成 O(n^2)。6.5 上可能存在问题。 (2认同)

sle*_*ske 102

您可以从子查询中创建派生表,并将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
Run Code Online (Sandbox Code Playgroud)

  • 或更一般地,SELECT*FROM表INNER JOIN otherTable ON(table.x = otherTable.a AND table.y = otherTable.b) (7认同)
  • 如果表2是表1的子节点,那么存在多行会怎么样?为什么LEFT JOIN? (4认同)
  • 是的, INNER JOIN 在这里会更高效。执行 LEFT JOIN 并过滤表 2 中的空值只是使用 INNER JOIN 的详细方法 (3认同)

gbn*_*gbn 12

一个简单的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
)
Run Code Online (Sandbox Code Playgroud)

如果在关联中有多行,则JOIN会在输出中提供多行,因此您需要使用不同的行.这通常使EXISTS更有效率.

注意带有JOIN的"SELECT"*也包括行限制表中的列


小智 12

select * from tab1 where (col1,col2) in (select col1,col2 from tab2)
Run Code Online (Sandbox Code Playgroud)

注意:
Oracle会忽略一个或多个所选列为NULL的行.在这些情况下,您可能希望使用NVL -Funktion将NULL映射到特殊值(不应该在值中);

select * from tab1
where (col1, NVL(col2, '---') in (select col1, NVL(col2, '---') from tab2)
Run Code Online (Sandbox Code Playgroud)

  • postgres在(...一些元组列表...)中支持`where(colA,colB)`,但是我不确定其他哪些数据库也可以这样做。我很想知道。 (2认同)
  • Oracle和DB2/400也支持这种语法(也可能是DB2).希望SQL Server支持它. (2认同)

Den*_*din 11

关于解决方案的警告:

如果行不是唯一的,许多现有解决方案将提供错误的输出

如果您是唯一创建表的人,那么这可能并不重要,但是当其中一个表可能不包含唯一行时,几种解决方案将提供与所讨论代码不同数量的输出行。

关于问题陈述的警告:

与多个列不存在,请仔细考虑您想要的

当我看到包含两列的输入时,我可以想象它意味着两件事:

  1. a列和b列的值独立出现在另一个表中
  2. a列和b列的值一起出现在另一个表中的同一行

方案1相当简单,只需使用两个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)
Run Code Online (Sandbox Code Playgroud)

左半连接(安全,建议用于支持它的方言)

这是一种非常简洁的连接方式,但是不幸的是,大多数SQL方言,包括SQL Server,目前都不支持它。

SELECT * FROM T1
LEFT SEMI JOIN T2 ON T1.a=T2.a and T1.b=T2.b
Run Code Online (Sandbox Code Playgroud)

多个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)
Run Code Online (Sandbox Code Playgroud)

没有代码重复的解决方案(我相信这在常规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)
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

最常见的错误:

  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)
Run Code Online (Sandbox Code Playgroud)

请注意,如果您的列是数字列,则某些SQL方言将要求您首先将它们转换为字符串。我相信SQL Server会自动执行此操作。


总结:像往常一样,在SQL中有很多方法可以做到这一点,使用安全的选择可以避免意外情况,从长远来看可以节省时间和精力。