【问题标题】:SQL - find rows that have the same set of values in a columnSQL - 查找列中具有相同值集的行
【发布时间】:2016-01-27 05:37:42
【问题描述】:

我有一个表,我想选择列中出现的具有完全相同的一组值的所有行,并将它们作为特定列的一对返回。

例如,假设我有一个名为 Table 的表:

C1  C2  
1   1  
1   2  
1   3  
2   1  
3   1  
3   2  
3   3  
4   1  
4   2  

当我运行查询时,它应该返回行:

1  3

因为这些是 C1 中的两个值,它们在 C2 列 (1,2,3) 中具有相同的一组值。

我在下面有一个不正确的查询,它返回在 C2 中至少有一个匹配值的所有行,我不知道如何更正它。

SELECT DISTINCT T1.C1, T2.C1
FROM Table T1, Table T2
WHERE T1.C1 != T2.C1 
AND T1.C2 = T2.C2
AND T1.C1 < T2.C1
GROUP BY s1.suppId, s2.suppId;  

任何帮助将不胜感激,谢谢。

【问题讨论】:

  • 如果将 5,1 和 5, 2 添加到表中,您希望看到什么?两行输出?
  • @michaelBroughton 这是正确的。这将产生另一行,第一列输出 4 列,第二列输出 5 列。
  • 如果你有三个 c1 和匹配的 C2 集怎么办?您希望动态显示三列?
  • cmets 的高方差表明,问题的表述不如唯一答案所需的清晰。这是我在不那么抽象的层面上的替代表述。该表包含两列person_idhobby_id(解释- 人A 有一个爱好H)。问题是找到所有具有相同爱好的人。一对只显示一次,因此如果 AB 有相同的爱好,则只显示 A,B,而不显示 B,A .如果 A、B 和 C 有相同的爱好,则显示三对:A、B; A、C 和 B、C。这是我在下面回答的。

标签: sql oracle


【解决方案1】:

listagg 是救援(而不是 @Juan 评论的计数)

 with lst as (
 select c1, LISTAGG(c2, '; ') WITHIN GROUP (ORDER BY c2) c2_lst
 from tst
 group by c1
 )
 select  lst1.c1 c1a, lst2.c1 c1b
 from lst lst1
 inner join lst lst2
 on lst1.c2_lst = lst2.c2_lst and
 lst1.c1 < lst2.c1
 ; 

按要求提供

1 3

SQLFiddleDemo

还要做好准备,这仅适用于小数据(连接的密钥限制为 4000 字节)。

更新

listagg 连接组中的所有值。要仅获取不同的值(即一组值),必须执行带有 DISTINCT 的附加查询。

 WITH lst AS
   ( SELECT DISTINCT c1,c2 FROM tst
   ),
   lst_dist AS
   (SELECT c1,
     LISTAGG(c2, '; ') WITHIN GROUP (
   ORDER BY c2) c2_lst
   FROM lst
   GROUP BY c1
   )
 SELECT lst1.c1 c1a,
   lst2.c1 c1b
 FROM lst_dist lst1
 INNER JOIN lst_dist lst2
 ON lst1.c2_lst = lst2.c2_lst
 AND lst1.c1    < lst2.c1

【讨论】:

  • 我不知道你有没有小提琴,所以我只更新我正在工作的那个。
  • WM_CONCAT 函数(如果包含在您的数据库中,Oracle 11.2 之前)或 LISTAGG(从 Oracle 11.2 开始)
  • 答案如何?该问题要求 c1 值具有 c2 中的所有值
  • @vkp - 由于 Q 不清楚,我制定了自己的版本(见上面的评论)并回答了它。随意做同样的事情:)
【解决方案2】:

正如您未指定的那样,这将处理两组匹配的 C2 值的情况,输出两行 - 每个匹配集一个。

with thetable as (    
SELECT   1 C1,  1 c2 from dual union 
SELECT   1 C1,   2   c2 from dual union
SELECT   1 C1,   3   c2 from dual union
SELECT   2 C1,   1   c2 from dual union
SELECT   3 C1,   1   c2 from dual union
SELECT   3 C1,   2   c2 from dual union
SELECT   3 C1,   3   c2 from dual union
SELECT   4 C1,   1   c2 from dual union
SELECT   4 C1,   2  c2 from dual union
-- added fourrows to give a second set of matches
SELECT   5 C1,   1   c2 from dual union
SELECT   5 C1,   2  c2 from dual union
SELECT   6 C1,   1  c2 from dual union
SELECT   6 C1,   2  c2 from dual )
SELECT LIST_C2, List_c1
FROM (
    SELECT list_c2
          ,LISTAGG(c1,',') WITHIN GROUP (ORDER BY c1) list_c1
    FROM (      
            SELECT c1, LISTAGG(c2,',') WITHIN GROUP (ORDER BY c2) list_c2 
            FROM thetable
            GROUP BY c1
         )
    group by list_c2        
    )
-- only bring back where we had more than one c1
WHERE instr(list_c1,',') != 0

显示以下两组C2值及其匹配的C1列表

LIST_C2  LIST_C1
"1,2"    "4,5,6"
"1,2,3"  "1,3"

【讨论】:

    【解决方案3】:

    您可以在没有列表 agg 的情况下使用自联接来执行此操作:

    select t1.c1, t2.c1
    from (select t.*, count(*) over (partition by c1) as cnt
          from table t
         ) t1 join
         (select t.*, count(*) over (partition by c1) as cnt
          from table t
         ) t2
         on t1.c2 = t2.c2 and t1.c1 < t1.c2 and t1.cnt = t2.cnt
    group by t1.c1, t2.c1
    having count(*) = max(t1.cnt);
    

    注意:这假设表中没有重复的行。在这种情况下也可以稍作改动。

    这将连接 second 列上的行,然后按第一列聚合。一路上,它确保两个表中匹配的列数相同,并且所有列都匹配。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-05-21
      • 2017-01-13
      • 1970-01-01
      • 2012-06-18
      • 2015-01-24
      • 2018-12-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多