SQL:删除可以置换列值的重复行

时间:2016-08-18 07:55:55

标签: sql oracle duplicates rows

想象一下有四列的表:A,B,C和D. 如果我有多个行,其中4个值相同(但它们可能位于不同的列中),如何只保留其中一行并删除其他行? (哪一个留下来并不重要)

示例:

A  B  C  D
----------
1  2  2  3 
3  2  1  2
2  1  2  3
2  2  1  3
1  8  8  8
8  1  8  8

结果应该是:

A  B  C  D
----------
1  2  2  3 
8  1  8  8

这个问题是否可能没有使用:scalar子查询,union / union all

允许:加入,CTE,rank / dense_rank / row_number / count,案例表达式

2 个答案:

答案 0 :(得分:2)

而不是@Plirkee提出的分层查询功能,您可以取消数据(子查询T1),使用函数list_agg()(子查询T2)中的正确排序对它们进行排序,以便查找对于每个唯一列表具有最小rowid的行,删除其余行:

delete from t where rowid not in (
  with t1 as (select rd, col, val
                from (select rowid rd, a, b, c, d from t) 
                unpivot (val for (col) in ("A", "B", "C", "D"))),
       t2 as (select rd, 
                     listagg(val, '-') within group (order by val) 
                       over (partition by rd) as list 
                from t1)
  select min(rd) from t2 group by list )

这适用于我的测试数据。如果列A,B,C,D是正数,则没有问题。如果它们是负面或字符串,可能会发生奇怪的事情,特别是包含' - '。在这种情况下,您应该使用listagg()的其他分隔符。

答案 1 :(得分:1)

好吧,我将借助于对给定字符串中的字符进行排序的函数来完成此操作。 所以首先让我们创建一个函数

create or replace function sortString(str in Varchar2) return varchar2 is
res  varchar2(500);
begin 
SELECT MIN(permutations) into res
FROM (SELECT REPLACE (SYS_CONNECT_BY_PATH (n, ','), ',') permutations
    FROM (SELECT LEVEL l, SUBSTR (str, LEVEL, 1) n
        FROM DUAL
        CONNECT BY LEVEL <= LENGTH (str)) yourtable
    CONNECT BY NOCYCLE l != PRIOR l)
WHERE LENGTH (permutations) = LENGTH (str);
return res;
end;

之后

DELETE FROM 
   table_name A
WHERE 
  a.rowid > 
   ANY (
     SELECT 
        B.rowid
     FROM 
        table_name B
     WHERE 
        sortString(A.col1 ||A.col2 || A.col3 ||A.col4)=sortString(B.col1 || B.col2 || B.col3 || B.col4)
        );

一些参考文献:sortString(1),delete duplicates(2)