我有一个包含字段 group_id 和 group_type 的表,我想在该表中查询元组列表中具有任何元组(group id,group type)的所有记录。例如,我希望能够做类似的事情:
SELECT *
FROM mytable
WHERE (group_id, group_type) IN (("1234-567", 2), ("4321-765", 3), ("1111-222", 5))
已经在以下位置提出了一个非常类似的问题: using tuples in sql in Clause ,但是那里提出的解决方案假设元组列表是从另一个表中获取的。这在我的情况下不起作用,因为元组值是硬编码的。
一种解决方案是使用字符串连接:
SELECT *
FROM mytable
WHERE group_id + STR(group_type, 1) IN ("1234-5672", "4321-7653", "1111-2225")
但问题是表很大,对每条记录进行字符串连接和转换会非常昂贵。
有什么建议吗?
经过非常小的调整(用单引号替换双引号并添加
VALUES
关键字),您建议的语法是有效的标准 SQL-92 语法,即
SELECT *
FROM mytable
WHERE (group_id, group_type) IN (
VALUES ('1234-567', 2),
('4321-765', 3),
('1111-222', 5)
);
截至 SQL Server 2022,Microsoft 尚未将其添加到 SQL Server 中,并将其视为“计划外”功能。
就其价值而言,PostgreSQL 和 SQLite 是支持此语法的 SQL 产品的示例。
在 SQL Server 2008 中你可以这样做:
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
)
编辑:这是一个过时的答案,尽管它是 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
元组确实存在,它很可能会以完全相同的方式实现它。
您可以使用通用表表达式来假装这些元组位于另一个表中:
;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)
使用该解决方案,这应该可行:
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)
顺便说一句,您可能应该使用 CTE 来创建该内表。
我还没见过这个,但类似的东西应该可以工作
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
这是另一个使用连接的元组解决方案:
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
我遇到了类似的问题,但我的元组集合是动态的 - 它是在查询参数中发送到 SQL Server 的。我想出了以下解决方案:
将元组作为 XML 传递:
DECLARE @tuplesXml xml = '<tuples><tuple group-id="1234-567" group-type="2"/><tuple group-id="4321-765" group-type="3"/></tuples>';
使用 XML 节点内连接要过滤的表:
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
在我的情况下似乎工作得很好,这比问题中描述的情况要复杂一些。
请记住,必须使用
t.*
而不是 *
,并且从 nodes
方法返回的表需要使用别名(在本例中为 tuple(col)
)。
select * from table_name where 1=1 and (column_a, column_b) in ((28,1),(25,1))