【问题标题】:Using tuples in SQL "IN" clause在 SQL“IN”子句中使用元组
【发布时间】:2011-11-04 09:02:01
【问题描述】:

我有一个包含字段 group_id 和 group_type 的表,我想从表中查询具有任何元组(group idgroup 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")

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

有什么建议吗?

【问题讨论】:

    标签: sql sql-server tuples row-value-expression


    【解决方案1】:

    给定一个非常小的调整(用单引号替换双引号并添加 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 和 consider it an 'unplanned' feature

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

    【讨论】:

    • 对此功能的反馈似乎已移至 UserVoice:feedback.azure.com/forums/908035-sql-server/suggestions/…
    • 那么哪些数据库支持这个?甲骨文?后格雷斯? MariaDB?
    • @static_rtti:我知道 PostgreSQL 和 Sqlite 支持语法,而 Oracle 和 mySQL 不支持。附:挑剔点:这些是 SQL 产品(或“SQL DBMS”或类似产品)的示例。数据库是用户使用 SQL 产品创建的变量。例如,有一个名为Airplanes(逻辑)的数据库保存在我笔记本电脑的磁盘上,我可以操作这个变量,它本身包含一个名为mytable 的表(逻辑)。数据库Airplanes 不支持上述语法,因为它受到我用来创建和修改它的 SQL 产品 (MSSQL) 的限制。
    • DB2 也支持这个。
    【解决方案2】:

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

    【讨论】:

    • SQL Server 2008 特有的哪些内容?是“来自价值观”的部分吗?
    • @Promather,是的,这是价值观。另一种方法是使用select ... union all,就像 B Tyler 的回答一样。
    • 太棒了!这在 SQL Server 2008 中可能吗?这清楚地显示了行值表达式的含义。
    • @MikaelEriksson 这也可以:where not exists (select T.group_id, T.group_type EXCEPT select * from (values ('1234-567', 2), ...) as V(group_id,group_type) ) ;
    • 或者这个:where exists (select T.group_id, T.group_type INTERSECT select * from (values ('1234-567', 2), ...) as V(group_id,group_type) ).
    【解决方案3】:

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

    【讨论】:

    • 好点!唯一的问题是,如果您有很长的元组列表。
    【解决方案4】:

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

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

    【讨论】:

    • 谢谢,为什么WITH子句前要加分号?
    • @Promather - 因为WITH 关键字可以有其他含义,并且要引入CTE,它必须是语句的开头。分号确保前面的语句绝对终止。
    【解决方案5】:

    使用该解决方案,这应该可以工作:

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

    【讨论】:

      【解决方案6】:

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

      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
      

      【讨论】:

        【解决方案7】:

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

        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
        

        【讨论】:

          【解决方案8】:

          我遇到了类似的问题,但我的元组集合是动态的 - 它在查询参数中被发送到 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))。

          【讨论】:

            【解决方案9】:
            select * from table_name where 1=1 and (column_a, column_b) in ((28,1),(25,1))
            

            【讨论】:

            • 此解决方案返回错误:An expression of non-boolean type specified in a context where a condition is expected, near ','
            猜你喜欢
            • 2010-12-01
            • 1970-01-01
            • 1970-01-01
            • 2013-10-16
            • 1970-01-01
            • 2019-04-29
            • 1970-01-01
            • 2014-10-01
            相关资源
            最近更新 更多