68

我有一个包含字段 group_id 和 group_type 的表,我想从元组列表中查询具有任何元组(组 id组类型)的所有记录。例如,我希望能够执行以下操作:

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")

但问题是表很大,对每条记录进行字符串连接和转换会非常昂贵。

有什么建议吗?

4

9 回答 9

74

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

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

遗憾的是,MSFT 没有将它添加到 SQL Server 中,并认为它是一个“计划外”功能

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

于 2011-11-04T14:48:44.577 回答
31

在 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-11-04T09:12:41.620 回答
17

编辑:这是一个过时的答案,虽然它是 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确实存在元组,它很可能会以完全相同的方式在幕后实现它。

于 2011-11-04T09:08:49.573 回答
7

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

;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-11-04T09:08:21.730 回答
3

使用该解决方案,这应该有效:

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来创建该内表。

于 2011-11-04T09:08:09.407 回答
2

我还没有看到这个,但这样的东西应该可以工作

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
于 2021-10-21T16:12:24.353 回答
0

这是另一个使用连接的元组解决方案:

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
于 2014-11-12T16:56:29.447 回答
0
select * from table_name where 1=1 and (column_a, column_b) in ((28,1),(25,1))
于 2019-05-31T16:07:16.083 回答
0

我遇到了类似的问题,但我的元组集合是动态的——它在查询参数中被发送到 SQL Server。我想出了以下解决方案:

  1. 将元组作为 XML 传递:

    DECLARE @tuplesXml xml = '<tuples><tuple group-id="1234-567" group-type="2"/><tuple group-id="4321-765" group-type="3"/></tuples>';
    
  2. 使用 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)在这种情况下)。

于 2018-12-06T22:41:26.740 回答