WHERE col1,col2 IN (...) [使用复合主键的 SQL 子查询]

Posted

技术标签:

【中文标题】WHERE col1,col2 IN (...) [使用复合主键的 SQL 子查询]【英文标题】:WHERE col1,col2 IN (...) [SQL subquery using composite primary key] 【发布时间】:2011-06-05 01:55:18 【问题描述】:

给定一个表foo 和一个复合主键(a,b),是否有用于编写查询的合法语法,例如:

SELECT ... FROM foo WHERE a,b IN (SELECT ...many tuples of a/b values...);
UPDATE foo SET ... WHERE a,b IN (SELECT ...many tuples of a/b values...);

如果这是不可能的,并且您无法修改架构,您如何执行与上述等效的操作?

我还将在此处添加术语“复合主键”、“子选择”、“子选择”和“子查询”,以用于这些别名的搜索命中。

编辑:我对标准 SQL 以及适用于 PostgreSQL 和 SQLite 3 的答案感兴趣。

【问题讨论】:

【参考方案1】:
sqlite> create table foo (a,b,c);
sqlite> create table bar (x,y);
sqlite> select * from foo where exists (select 1 from bar where foo.a = bar.x and foo.b = bar.y);

select 1 from bar 替换为您的select ... many tuples of a/b values ...

或者为您的select ... many tuples of a/b values ... 创建一个临时表并使用它来代替bar..

【讨论】:

【参考方案2】:

您的语法非常接近标准 SQL!

以下是有效的 FULL SQL-92(由Mimer SQL-92 Validator 确认)

SELECT * 
  FROM foo 
  WHERE (a, b) IN (
                   SELECT a, b 
                      FROM bar
                  );

当然,并不是每个 SQL 产品都支持完整的 SQL-92(耻辱!)如果有人希望看到 Microsoft SQL Server 支持这种语法,他们可以投票给它here。

另一个更广泛支持的 SQL-92 结构(例如 Microsoft SQL Server 和 Oracle)是 INTERSECT,例如

SELECT a, b
  FROM Foo
INTERSECT
SELECT a, b
  FROM Bar;

请注意,这些构造可以正确处理 NULL 值,这与此处的其他一些建议不同,例如那些使用EXISTS (<equality predicates>)、连接值等的人。

【讨论】:

PostgreSql 支持此功能。 SELECT * FROM foo WHERE (a,b) IN ((1,2),(3,4)) 仅供参考,INTERSECT 的反面是 EXCEPT【参考方案3】:

你犯了一个很小的错误。 您必须将 a,b 放在括号中。

SELECT ... FROM foo WHERE (a,b) IN (SELECT f,d FROM ...);

这行得通!

【讨论】:

【参考方案4】:

您建议的 IN 语法不是有效的 SQL。使用 EXISTS 的解决方案应该适用于所有合理兼容的 SQL RDBMS:

 UPDATE foo SET x = y WHERE EXISTS
    (SELECT * FROM bar WHERE bar.c1 = foo.c1 AND bar.c2 = foo.c2)

请注意,这通常不是特别高效。

【讨论】:

IN 和 EXISTS 应该产生相同的计划,因为它们在语义上是相同的。至少在 SQL Server 中,无论如何你都会得到相同的计划。【参考方案5】:
    SELECT ...
      FROM foo
INNER JOIN (SELECT ...many tuples of a/b values...) AS results
        ON results.a = foo.a
       AND results.b = foo.b

你在找什么?

【讨论】:

看起来不错,只是它不适用于 SQLite3 上的 UPDATE,does not support joins in UPDATE 查询。我试图部分了解核心多键 IN 是否合法(我刚刚读到它不在 SQLite 中),但也是为了帮助answer this question。【参考方案6】:

通过串联,这适用于 PostgreSQL:

SELECT a,b FROM foo WHERE a||b IN (SELECT a||b FROM bar WHERE condition);

UPDATE foo SET x=y WHERE a||b IN (SELECT a||b FROM bar WHERE condition);

【讨论】:

这是个坏主意,取决于 a 和 b 的值。您的串联并不总是唯一的。您可以有一条记录 a=22 和 b=1,另一条记录 a=2 和 b=21。两者的串联都是 221!为了避免这个问题,a||'-'||b 是稍微好一点的串联。另一个问题是,使用连接时,查询会很慢,因为数据库无法以最佳方式使用它的索引。【参考方案7】:

如果您需要一个不需要表中已存在值的元组的解决方案,您可以连接列表中的相关表值和项目,然后使用“IN”命令。

在 postgres 中看起来像这样:

SELECT * FROM foo WHERE a || '_' || b in ('Hi_there', 'Me_here', 'Test_test');

在 SQL 中,我想它可能看起来像这样:

SELECT * FROM foo WHERE CONCAT(a, "_", b) in ('Hi_there', 'Me_here', 'Test_test');

【讨论】:

【参考方案8】:

JOINSINTERSECTS 可以很好地替代IN,但它们作为NOT IN 的替代品并不那么明显,例如:将行从TableA 插入到TableB 中它们没有的地方'在TableB 中尚不存在,其中两个表上的PK 是一个组合。

我目前在 SQL Server 中使用上面的连接方法,但这不是一个非常优雅的解决方案。

【讨论】:

建议你看看MERGE..USING..WHEN NOT MATCHED THEN INSERT...【参考方案9】:

Firebird 使用这个连接公式:

SELECT a,b FROM foo WHERE a||b IN (SELECT a||b FROM bar WHERE 条件);

【讨论】:

以上是关于WHERE col1,col2 IN (...) [使用复合主键的 SQL 子查询]的主要内容,如果未能解决你的问题,请参考以下文章

SQL优化案例之where exists(col1=xxx or col2=xxx)等价改写

SQL优化案例之where exists(col1=xxx or col2=xxx)等价改写

SQL优化案例之where exists(col1=xxx or col2=xxx)等价改写

SQL优化案例之where exists(col1=xxx or col2=xxx)等价改写

Hibernate select with AND condition and all AND in OR

mysql删除重复数据