【问题标题】:Finding the id's which include multiple criteria in long format查找包含多个长格式标准的 id
【发布时间】:2022-01-25 07:08:52
【问题描述】:

假设我有一张这样的桌子,

id tagId
1 1
1 2
1 5
2 1
2 5
3 2
3 4
3 5
3 8

我想选择 id 的,其中 tagId 包括 2 和 5。对于这个假数据集,它应该返回 1 和 3。

我试过了,

select id from [dbo].[mytable] where tagId IN(2,5)

但它分别考虑了 2 和 5。我也不想让我的表格保持宽格式,因为 tagId 是动态的。它可以达到任意数量的列。我还考虑使用两个不同的查询进行过滤以找到(以某种方式)交集。但是,由于我在现实生活中可能会在 tagId 中搜索两个以上的值,所以这对我来说效率低下。

我确信这是标签搜索之前遇到的问题。你有什么建议?更改表格格式?

【问题讨论】:

    标签: sql sql-server select relational-division


    【解决方案1】:

    这实际上是一个Relational Division With Remainder 的问题。

    首先,您必须将输入放入正确的表格格式。如果从客户端代码执行,我建议您使用表值参数。您还可以使用临时表或表变量。

    DECLARE @ids TABLE (tagId int PRIMARY KEY);
    INSERT @ids VALUES (2), (5);
    

    这类问题有多种不同的解决方案。

    1. 经典双否定EXISTS

      SELECT DISTINCT
        mt.Id
      FROM mytable mt
      WHERE NOT EXISTS (SELECT 1
          FROM @ids i
          WHERE NOT EXISTS (SELECT 1
              FROM mytable mt2
              WHERE mt2.id = mt.id
                AND mt2.tagId = i.tagId)
      );
      

      这通常不是很有效

    2. 与要匹配的 ID 总数相比

      SELECT mt.id
      FROM mytable mt
      JOIN @ids i ON i.tagId = mt.tagId
      GROUP BY mt.id
      HAVING COUNT(*) = (SELECT COUNT(*) FROM @ids);
      

      这样效率更高。您也可以使用窗口函数来执行此操作,它的效率可能更高或更低,YMMV。

      SELECT mt.Id
      FROM mytable mt
      JOIN (
          SELECT *,
            total = COUNT(*) OVER ()
          FROM @ids i
      ) i ON i.tagId = mt.tagId
      GROUP BY mt.id
      HAVING COUNT(*) = MIN(i.total);
      
    3. 另一种解决方案涉及交叉连接所有内容并使用条件聚合检查有多少匹配项

      SELECT mt.id
      FROM (
          SELECT
            mt.id,
            mt.tagId,
            matches = SUM(CASE WHEN i.tagId = mt.tagId THEN 1 END),
            total = COUNT(*)
          FROM mytable mt
          CROSS JOIN @ids i
          GROUP BY
            mt.id,
            mt.tagId
      ) mt
      GROUP BY mt.id
      HAVING SUM(matches) = MIN(total)
         AND MIN(matches) >= 0;
      

    db<>fiddle

    还有其他解决方案,见High Performance Relational Division in SQL Server

    【讨论】:

    • 这是非常有用的。谢谢@Charlieface。我希望我有机会同时接受两个答案。但赞成!
    【解决方案2】:

    一种选择是计算每个id 具有的不同tagIds(来自您正在寻找的)的数量:

    SELECT   id
    FROM     [dbo].[mytable] 
    WHERE    tagId IN (2,5)
    GROUP BY id
    HAVING   COUNT(DISTINCT tagId) = 2
    

    【讨论】:

    • 聪明的解决方案。小错字:DISTINCT.
    • 最后一步的数字 2 代表我正在搜索的标签数量的至少条件。对吗?
    • @Zaki 确实是一个错字,感谢您的注意。编辑和修复
    • @maydin where 子句将返回行限制为 tagId 为 2 和 5,然后 having 子句确保它们都存在。如果一个 ID 有额外的标签,它不会干扰这个查询
    • 你是救生员@Mureinik。谢谢!
    猜你喜欢
    • 1970-01-01
    • 2012-11-18
    • 1970-01-01
    • 2023-04-05
    • 2023-01-24
    • 2015-06-14
    • 1970-01-01
    • 2010-10-05
    • 1970-01-01
    相关资源
    最近更新 更多