【问题标题】:How do I determine if a group of data exists in a table, given the data that should appear in the group's rows?给定应该出现在组行中的数据,我如何确定表中是否存在一组数据?
【发布时间】:2014-02-28 19:57:02
【问题描述】:

我正在将数据写入表并为每批写入的数据分配一个“组 ID”。为了说明,请考虑下表。

GroupId  Value
-------  -----
      1      a
      1      b
      1      c
      2      a
      2      b
      3      a
      3      b
      3      c
      3      d

在此示例中,有三组数据,每组数据具有相似但不同的值。

如何查询此表以查找包含给定值集的组?例如,如果我查询 (a,b,c),结果应该是第 1 组。同样,查询 (b,a) 应该导致第 2 组,查询 (a,b,c,e)应该导致空集。

我可以编写一个执行以下步骤的存储过程:

  • 从 Groups 中选择不同的 GroupId -- 并在本地存储
  • 对于每个不同的 GroupId:在输入值和表值(对于组)之间执行集合差异 (except),反之亦然
  • 如果两个集合差异操作产生空集合,则返回 GroupId

这似乎有点过分,我希望利用 SQL 中的一些其他命令来简化。有没有更简单的方法在这种情况下执行集合比较,或者选择包含查询的确切输入值的组 ID?

【问题讨论】:

    标签: sql sql-server grouping set-operations


    【解决方案1】:

    这是一个集合内集合查询。我喜欢使用group byhaving 来解决它:

    select groupid
    from GroupValues gv
    group by groupid
    having sum(case when value = 'a' then 1 else 0 end) > 0 and
           sum(case when value = 'b' then 1 else 0 end) > 0 and
           sum(case when value = 'c' then 1 else 0 end) > 0 and
           sum(case when value not in ('a', 'b', 'c') then 1 else - end) = 0;
    

    having 子句中的前三个条件检查每个元素是否存在。最后一个条件检查没有其他值。此方法非常灵活,适用于您要查找的值的各种排除和包含条件。

    编辑:

    如果你想传入一个列表,你可以使用:

    with thelist as (
          select 'a' as value union all
          select 'b' union all
          select 'c'
         )
    select groupid
    from GroupValues gv left outer join
         thelist
         on gv.value = thelist.value
    group by groupid
    having count(distinct gv.value) = (select count(*) from thelist) and
           count(distinct (case when gv.value = thelist.value then gv.value end)) = count(distinct gv.value);
    

    这里的having 子句计算匹配值的数量并确保它与列表的大小相同。

    编辑: 查询编译失败,因为缺少表别名。使用正确的表别名更新。

    【讨论】:

    • 用户用户如何传递感兴趣的值集?因为感兴趣的集合值是动态的而不是固定的。
    • +1:我几乎肯定它的性能会比我的意大利面条逻辑更好。 :)
    • 这很聪明!如果我的值是外键并且保证在组中是唯一的,我相信我可以进一步简化。如果是这种情况,我应该能够检查 a 组大小是否与我的输入集大小匹配。这是声音吗?
    • @SteveGuidi。 . .我认为您不能像这样简化having 子句。您需要两个组之间的完全匹配。因此,您需要确保它们的大小相同并且没有缺失值。
    【解决方案2】:

    这有点难看,但它确实有效。在较大的数据集上,我不确定性能会是什么样子,但是 #GroupValues 的嵌套实例在主表中关闭 GroupID 所以我认为只要你在 GroupID 上有一个好的索引它可能不会不要太可怕。

    If      Object_ID('tempdb..#GroupValues') Is Not Null Drop Table #GroupValues
    Create  Table #GroupValues (GroupID Int, Val Varchar(10));
    Insert  #GroupValues (GroupID, Val)
    Values  (1,'a'),(1,'b'),(1,'c'),(2,'a'),(2,'b'),(3,'a'),(3,'b'),(3,'c'),(3,'d');
    
    If      Object_ID('tempdb..#FindValues') Is Not Null Drop Table #FindValues
    Create  Table #FindValues (Val Varchar(10));
    Insert  #FindValues (Val)
    Values  ('a'),('b'),('c');
    
    Select  Distinct gv.GroupID
    From   (Select  Distinct GroupID 
            From    #GroupValues) gv
    Where   Not Exists (Select  1
                        From    #FindValues fv2
                        Where   Not Exists (Select  1
                                            From    #GroupValues gv2
                                            Where   gv.GroupID = gv2.GroupID
                                            And     fv2.Val = gv2.Val))
    And     Not Exists (Select  1
                        From    #GroupValues gv3
                        Where   gv3.GroupID = gv.GroupID
                        And     Not Exists (Select  1
                                            From    #FindValues fv3
                                            Where   gv3.Val = fv3.Val))
    

    【讨论】:

    • 也就是说,这或多或少是您所描述的放入存储过程的单个查询版本。
    猜你喜欢
    • 2014-03-08
    • 2013-03-22
    • 1970-01-01
    • 1970-01-01
    • 2011-09-25
    • 2021-01-28
    • 1970-01-01
    • 2021-04-15
    • 1970-01-01
    相关资源
    最近更新 更多