在 SQL“ IN”子句中使用元组

我有一个包含 group _ id 和 group _ type 字段的表,我希望查询该表,从元组列表中查询所有具有任何元组(组别编号团体类型)的记录。例如,我希望能够这样做:

SELECT *
FROM mytable
WHERE (group_id, group_type) IN (("1234-567", 2), ("4321-765", 3), ("1111-222", 5))

在子句中使用 sql 中的元组中已经提出了一个非常类似的问题,但是提出的解决方案假定元组列表是从另一个表中获取的。这在我的情况下不工作是元组值是硬编码的。

一种解决方案是使用字符串连接:

SELECT *
FROM mytable
WHERE group_id + STR(group_type, 1) IN ("1234-5672", "4321-7653", "1111-2225")

但问题是这个表非常大,并且为每条记录执行字符串串联和转换将非常昂贵。

有什么建议吗?

77997 次浏览

使用这种解决方案,应该会奏效:

SELECT *
FROM mytable m
WHERE EXISTS (
SELECT * FROM (
SELECT "1234-567" group_id, 2 group_type UNION ALL
SELECT "4321-765", 3 UNION ALL
SELECT "1111-222", 5) [t]
WHERE m.group_id = t.group_id AND m.group_type = t.group_type)

顺便说一下,您可能应该使用 慢性创伤性脑病来创建内部表。

您可以使用一个公共表表达式来假装这些元组在另一个表中:

;WITH Tuples as (
select '1234-567' as group_id, 2 as group_type union all
select '4321-765', 3 union all
select '1111-222', 5
)
SELECT * /* TODO - Pick appropriate columns */
from mytable m where exists (
select * from Tuples t
where m.group_id = t.group_id and m.group_type = t.group_type)

编辑: 这是一个过时的答案,虽然它是在2011年被接受的答案,其他答案与更多的赞成票反映了更新的方法。

为什么不构造 OR 语句?

SELECT *
FROM mytable
WHERE (group_id = '1234-567' and group_type = 2)
OR (group_id = '4321-765' and group_type = 3)
OR (group_id = '1111-222' and group_type = 5)

当然,它看起来不像您的概念示例那样漂亮和整洁,但是它可以完成这项工作(如果您的 IN确实存在元组,那么它很可能以完全相同的方式实现它。

在 SQLServer2008中,您可以这样做:

select *
from mytable as T
where exists (select *
from (values ('1234-567', 2),
('4321-765', 3),
('1111-222', 5)) as V(group_id, group_type)
where T.group_id = V.group_id and
T.group_type = V.group_type
)

给定一个非常小的调整(将双引号替换为单引号并添加 VALUES关键字) ,您提出的语法是有效的标准 SQL-92语法,即。

SELECT *
FROM mytable
WHERE (group_id, group_type) IN (
VALUES ('1234-567', 2),
('4321-765', 3),
('1111-222', 5)
);

遗憾的是,MSFT 没有将其添加到 SQLServer 和 把它看作是一个“计划外”的功能中。

FWIWPostgreSQL 和 Sqlite 就是支持这种语法的 SQL 产品的例子。

下面是另一个使用 join 的 tuple 解决方案:

SELECT
*
FROM mytable m
JOIN
(
SELECT "1234-567" group_id, 2 group_type
UNION ALL SELECT "4321-765", 3
UNION ALL SELECT "1111-222", 5
) [t]
ON m.group_id = t.group_id
AND m.group_type = t.group_type

我也遇到过类似的问题,但是我的 tuple 集合是动态的——它以查询参数的形式发送到 SQLServer。我想出了以下解决方案:

  1. 将 tuple 作为 XML 传递:

    DECLARE @tuplesXml xml = '<tuples><tuple group-id="1234-567" group-type="2"/><tuple group-id="4321-765" group-type="3"/></tuples>';
    
  2. Inner join the table that you want to filter with the XML nodes:

    SELECT t.* FROM mytable t
    INNER JOIN @tuplesXml.nodes('/tuples/tuple') AS tuple(col)
    ON tuple.col.value('./@group-id', 'varchar(255)') = t.group_id
    AND tuple.col.value('./@group-type', 'integer') = t.group_type
    

It seems to work fine in my situation which is a bit more complex than the one described in the question.

Keep in mind that it is necessary to use t.* instead of * and the table returned from nodes method needs to be aliased (it's tuple(col) in this case).

select * from table_name where 1=1 and (column_a, column_b) in ((28,1),(25,1))

我还没见过这个,但这样的东西应该能行

SELECT * FROM  AgeGroup ag JOIN
(VALUES
('18-24', 18, 24),
('25-34 ', 25, 39),
('35-44 ', 35, 49),
('45-54 ', 45, 59),
('55-64 ', 55, 69),
('65+   ', 65, 299)
) AS x (agegroup, minage, maxage)
ON ag.age_group = x.agegroup
AND ag.min_age=x.minage
AND ag.max_age=x.maxage