简体   繁体   English

使用sql或plsql在Oracle中进行条件删除

[英]Conditional delete in oracle using sql or plsql

We have an oracle table x and we have values in it in a following manner 我们有一个oracle表x,并以下面的方式在其中包含值

num1  dt        cd
1   24-06-2017  3
1   24-06-2017  4
2   24-06-2017  1
2   24-06-2017  2
2   24-06-2017  4
2   25-06-2017  3
2   25-06-2017  4

Now I have to delete duplicate data from this table using num1,dt , if num1 and dt have any other value instead of 4 then it should exist in table and cd=4 should be deleted. 现在,我必须使用num1,dt从此表中删除重复数据,如果num1和dt的值不是4,则应存在于表中,并且应该删除cd = 4。 Here in below condition 3 or any other value should exist but it should not be 4 在下面的条件3中,应该存在任何其他值,但不应为4

1   24-06-2017  3
1   24-06-2017  4

Now here 4 should be deleted completely for 2 , 24/06/2017 and only 1 rows of each field should exist in the table ie :2 24/06/2017 1 and 2 24/06/2017 2 should exist. 现在,此处4应该在2,24/06/2017中被完全删除,并且表中每个字段仅应存在1行,即:2 24/06/2017 1和2 24/06/2017 2应该存在。

2   24-06-2017  1
2   24-06-2017  2
2   24-06-2017  4

Now in third case if we dont have any other value except 4 for num1 and date then we should have only 1 row of 4 for these dates. 现在在第三种情况下,如果num1和date除了4以外没有其他值,则这些日期应该只有4的1行。

3   26-06-2017  4
3   26-06-2017  4

Please provide solution for this, help will be appreciated. 请为此提供解决方案,我们将不胜感激。

If I get your question right it might work like this: 如果我正确回答您的问题,则可能会这样进行:

DELETE FROM table
WHERE (num1, dt, ct) IN (SELECT num1, dt, 4
                         FROM table
                         GROUP BY num1, dt
                         HAVING MAX(ct) != 4
                             OR MIN(ct) != 4)
  OR ((num, dt, ct) IN (SELECT num1, dt, MAX(ct)
                FROM table
                GROUP BY num1, dt
                HAVING MAX(ct) = 4
                   AND MIN(ct) = 4)
      AND rowid NOT IN (SELECT MIN(rowid)
                        FROM table
                        GROUP BY num1, dt
                        HAVING MAX(ct) = 4
                           AND MIN(ct) = 4))

Oracle conveniently has rowid , which can really help with this. Oracle方便地具有rowid ,它可以真正帮助您。 Here is an approach that uses rowid : 这是一种使用rowid的方法:

delete from t
    where t.rowid <> (select max(rowid) keep (dense_rank first
                                              order by (case when cd = 4 then 2 else 1 end)
                                             )
                      from t
                      where t2.num1 = t.num1 and t2.date = t.date
                     );

This will keep exactly one row per num1 / date comparison. 每个num1 / date比较将只保留一行。

If you are using Oracle 12C+, then a more intuitive form of this query is: 如果使用的是Oracle 12C +,则此查询的更直观形式为:

delete from t
    where t.rowid <> (select rowid
                      from t
                      where t2.num1 = t.num1 and t2.date = t.date
                      order by (case when cd = 4 then 2 else 1 end)
                      fetch first 1 row only
                     );

This doesn't work in earlier versions because fetching one row requires an additional subquery -- and that subquery cannot be correlated with the outer query. 这在早期版本中不起作用,因为获取一行需要附加的子查询-且该子查询无法与外部查询相关联。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM