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】:JOINS
和 INTERSECTS
可以很好地替代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)等价改写