【问题标题】:Finding a matching list of items in SQL在 SQL 中查找匹配的项目列表
【发布时间】:2014-04-17 19:45:13
【问题描述】:

我有一个表和另一个表中的相关属性列表。它看起来像这样:

表格单位

ID | State | ...
----------------
 1 |    10 | ...
 2 |    15 | ...
 3 |    10 | ...

(这里的状态没有意义,我只是添加它以表明该表包含额外的列)

表格形状

ID | Unit_ID (FK) | Shape
-------------------------
 1 |            1 |    40
 2 |            1 |    40
 3 |            1 |    45
 4 |            3 |    10

所以基本上Unit 中的每一行都可以有各种相关的形状。形状的顺序并不重要,但一个形状可以关联多次。

在我的示例中,Units 具有以下形状:

  • Unit 1: 40, 40, 45
  • Unit 2:无
  • Unit 3: 10

到目前为止一切顺利。这在获取/更新给定Unit-ID 的完整形状集时效果很好。

但我需要选择所有具有特定关联Shapes 列表的Units。例如,我想查找所有具有Shapes 40, 40, 45Units(应该返回Unit_ID 1)。

我已经找到了一些使用关系除法的类似问题和文章,但我不确定这是否可行,因为大多数解决方案无法处理多次出现的值。如果我搜索40, 45,我想要完全匹配,我希望查询在我的示例中不返回任何结果,因为没有UnitShapes 完全匹配(我需要一个查询来处理这个情况也是如此 - 选择包含子集的 UnitsShapes - 但是一旦我知道如何搜索精确匹配,这可能会很容易)。

我使用的是 SQL Server 2012。

【问题讨论】:

    标签: sql sql-server relational-algebra


    【解决方案1】:

    下面的方法首先将要查找的列表放在一个表中(嗯,一个 CTE),其中包含每个形状和次数的计数。然后,它会进行各种检查。

    首先是shapes 中的每个形状出现正确的次数。这就是内部聚合的来源。

    然后,它计算匹配的形状数量并验证这是tofind中不同形状的总数:

    with tofind as (
          select 40 as shape, 2 as cnt union all
          select 45 as shape, 1 as cnt
         )
    select s.unit_id
    from (select s.unit_id, tofind.cnt as tf_cnt, count(s.id) as s_cnt
          from shapes s join
               tofind
               on s.shape = tofind.shape
          group by s.unit_id, tofind.shape, tofind.cnt
         ) s
    group by s.unit_id
    having sum(case when s_cnt = tf_cnt then 1 else 0 end) = (select count(*) from tofind);
    

    编辑:

    Here 是一个 SQL Fiddle,它展示了它的工作原理。然而,上面的代码并没有寻找完全匹配,因为其他形状可能在记录中。以下修改仅适用于完全匹配:

    with tofind as (
          select 40 as shape, 2 as cnt union all
          select 45 as shape, 1 as cnt
         )
    select s.unit_id
    from (select s.unit_id, tofind.cnt as tf_cnt, count(s.id) as s_cnt
          from shapes s left outer join
               tofind
               on s.shape = tofind.shape
          group by s.unit_id, tofind.shape, tofind.cnt
         ) s
    group by s.unit_id
    having sum(case when s_cnt = tf_cnt then 1 else 0 end) = (select count(*) from tofind) and
           count(*) = sum(case when s_cnt = tf_cnt then 1 else 0 end);
    

    区别在于left outer joinhaving 子句中的附加条件。

    【讨论】:

    • 听起来很合理,但是子选择失败了,因为tofind.cnt 不是聚合或 GROUP BY 的一部分。我尝试将它添加到内部 GROUP BY,但它不会产生正确的结果 - 如果我尝试在 (40, 40, 45) 中搜索 (45),它会返回一个 ID。
    • 非常感谢,您的第二个示例似乎有效,我将在我的服务器上尝试一下。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-12-30
    • 1970-01-01
    • 2012-10-31
    • 1970-01-01
    • 2022-11-12
    • 1970-01-01
    • 2021-12-03
    相关资源
    最近更新 更多