【问题标题】:Conditional duplicate removal in Oracle 10gOracle 10g 中的条件重复删除
【发布时间】:2015-01-16 06:52:49
【问题描述】:
CREATE TABLE testdup
(seq_no NUMBER,
ID NUMBER,
attrib1 NUMBER,
attrib2 NUMBER);

INSERT INTO testdup
     VALUES (2, 15, 1211, 1250);
INSERT INTO testdup
     VALUES (1, 15, -999, -999);
INSERT INTO testdup
     VALUES (3, 16, 1234, 1234);
INSERT INTO testdup
     VALUES (4, 16, 1234, -1234);
INSERT INTO testdup
     VALUES (5, 17, -999, -999);
INSERT INTO testdup
     VALUES (6, 17, -999, -999);
INSERT INTO testdup
     VALUES (7, 18, -999, -999);
INSERT INTO testdup
     VALUES (8, 19, 741, -715);

COMMIT ;

我需要做的是,删除重复的 id -

  • 如果重复的 ids 记录具有值(attrib1= 除 -999 之外的任何值),则删除较低的 seq_no(在上面的示例中 id=16)
  • 如果 -999 与非 -999 一起存在(在上面的示例中 id=15)​​,则删除值为 -999 的记录
  • 如果所有记录都存在重复记录值“attrib1=-999”,则删除较低的 seq_no(在上面的示例中 id=17)
  • 跳过不重复的 id(在上面的示例中 id=18 和 19)

在上面的例子中,seq_no 应该删除 1、3 和 5

数据库 - Oracle 10g

下面的查询给了我部分输出,但是当给定 id 的 attrib1 相同时,删除 min(seq_id) 不起作用

SELECT seq_no, ID, attrib1,
       ROW_NUMBER () OVER (PARTITION BY ID, ID ORDER BY CASE
           WHEN attrib1 = -999
              THEN 999999999
           ELSE TO_NUMBER (attrib1)
        END) rn
  FROM testdup order by 1

我正在玩分析函数并找到了解决方案,将其附在此处供其他人参考

SELECT seq_no, ID, attrib1,
       ROW_NUMBER () OVER (PARTITION BY ID ORDER BY CASE
           WHEN attrib1 = -999
              THEN 999999999
           ELSE TO_NUMBER (attrib1)
        END ASC,
        seq_no DESC) rn
  FROM testdup

【问题讨论】:

  • 你的问题是什么?
  • attrib2 是否会以某种方式影响所需的删除?如果我正确理解了您的要求,则不会。
  • @René Nyffenegger - attrib2 与删除无关
  • @Jens - 我正在尝试分区,但似乎卡住了
  • id=17 同时拥有attrib1=-999,这与您在问题中所说的相反

标签: sql oracle


【解决方案1】:

我相信你想要

delete from testdup where rowid in (
  select
    coalesce(
      case when rowid_999_min is not null and 
                rowid_999_max is not null and
                rowid_999_min != rowid_999_max 
           then null
           else rowid_999_max
      end,
      rowid_min_seq
  )
  from (
      select
        min(case when attrib1 = -999 then rowid end)        rowid_999_min,
        max(case when attrib1 = -999 then rowid end)        rowid_999_max,
        min(rowid) keep (dense_rank first order by seq_no)  rowid_min_seq
      from
        testdup
      group by
        id
      having
        count(*) > 1
  )
);

【讨论】:

    【解决方案2】:

    这是一个尝试:

    with w as
    (
      select t.id,
             case when sum(case when t.attrib1 = -999 then 1 else 0 end) > 0 then 1 else 0 end exists999,
             case when min(t.attrib1) = -999 and max(t.attrib1) = -999 then 1 else 0 end only999
      from testdup t
      group by t.id
      having count(*) > 1
    )
    select 'Only -999 values, removed min seq_no' reason, min(t.seq_no) removed
    from testdup t, w
    where w.id = t.id
      and w.only999 = 1
    group by t.id
    
    union all
    
    select 'No -999 values, removed min seq_no' reason, min(t.seq_no) removed
    from testdup t, w
    where w.id = t.id
      and w.exists999 = 0
    group by t.id
    
    union all
    
    select 'Some -999 values, removed seq_no with this value' reason, t.seq_no removed
    from testdup t, w
    where w.id = t.id
      and w.exists999 = 1
      and w.only999 = 0
      and t.attrib1 = -999
    ;
    

    with 子句允许我知道一组相似的 ID,它们是否仅包含 -999 值。然后,我会根据您的每个条件进行一个查询。

    结果:

        REASON                                 REMOVED
    1   Only -999 values, removed min seq_no                5
    2   No -999 values, removed min seq_no                  3
    3   Some -999 values, removed seq_no with this value    1
    

    【讨论】:

    • 有趣的方法..它给出了预期的结果:)
    猜你喜欢
    • 2015-10-13
    • 1970-01-01
    • 2017-11-23
    • 2020-02-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-19
    相关资源
    最近更新 更多