SQL删除不在另一个表中的行

时间:2017-05-04 08:29:28

标签: oracle sql-delete

我正在寻找一个好的SQL方法(Oracle数据库)来满足下一个要求:

  1. 表B <
  2. 中删除表A 中的行
  3. 两个表都具有相同的结构
  4. 某些字段可以为空
  5. 列和行的数量巨大(更多100k行和20-30列要比较)
  6. 每一行的每个字段都需要从表A 表B进行比较。
  7. 此要求是由于必须每天运行的流程,因为更改将来自表B
  8. 换句话说:表A减去表B =&gt;从表A

    中删除记录
    delete from Table A
               where (field1, field2, field3) in
                     (select field1, field2, field3
                        from Table A
    
                      minus
                      select field1, field2, field3
                        from Table B);
    

    非常重要的是要提到 DELETE 子句中的正常 MINUS 会失败,因为不考虑可空字段上的空值(oracle的未知结果) ,然后没有比赛)。

    我也成功尝试了 EXISTS ,但是我必须使用NVL函数将空值替换为虚拟值,我不想要它,因为我不能保证将值替换为NVL不会成为该领域的有效值。

    有人知道如何完成这样的事情吗?请记住性能和可空的字段为&#34; a must&#34;。

    感谢

6 个答案:

答案 0 :(得分:1)

在where子句中使用左外连接并测试null

删除一个   来自a        左外连接b在a.x = b.x上 其中b.x为空

答案 1 :(得分:1)

您是否考虑过ORALCE SQL MERGE声明?

答案 2 :(得分:1)

decode找到相同性(即使两个值都为null):

decode( field1, field2, 1, 0 ) = 1  

删除table2中找不到的表:

delete table1 t
 where t.rowid in (select t1.rowid
                     from table1 t1
                     left outer join table2 t2
                       on decode(t1.field1, t2.field1, 1, 0) = 1
                      and decode(t1.field2, t2.field2, 1, 0) = 1
                      and decode(t1.field3, t2.field3, 1, 0) = 1
                      /* ... */
                      where t2.rowid is null /* no matching row found */
                      ) 

使用现有索引

                      ...
                      left outer join table2 t2
                      on (t1.index_field1=t2.index_field1 or 
                          t1.index_field1 is null and t2.index_field1 is null)
                      and ...

答案 3 :(得分:0)

对大量记录使用批量操作。性能方面它会更快。

并使用两个表之间的连接来获取要删除的行。可以将可以为空的列与某个默认值进行比较。

另外,如果您希望表A与表B类似,为什么不截断表A然后从表b插入数据

答案 4 :(得分:0)

假设您在每个表上都有相同的PK字段......(拥有PK或其他一些唯一密钥对此至关重要。)

    create table table_a (id number, name varchar2(25), dob date);

    insert into table_a values (1, 'bob', to_date('01-01-1978','MM-DD-YYYY'));

    insert into table_a values (2, 'steve', null);

    insert into table_a values (3, 'joe', to_date('05-22-1989','MM-DD-YYYY'));

    insert into table_a values (4, null, null);

    insert into table_a values (5, 'susan', to_date('08-08-2005','MM-DD-YYYY'));

    insert into table_a values (6, 'juan', to_date('11-17-2001', 'MM-DD-YYYY'));

    create table table_b (id number, name varchar2(25), dob date);

    insert into table_b values (1, 'bob', to_date('01-01-1978','MM-DD-YYYY'));

    insert into table_b values (2, 'steve',to_date('10-14-1992','MM-DD-YYYY'));

    insert into table_b values (3, null, to_date('05-22-1989','MM-DD-YYYY'));

    insert into table_b values (4, 'mary', to_date('12-08-2012','MM-DD-YYYY'));

    insert into table_b values (5, null, null);

    commit;

    -- confirm minus is working
    select id, name, dob 
    from   table_a
    minus
    select id, name, dob
    from   table_b;

    -- from the minus, re-query to just get the key, then delete by key
    delete table_a where id in (
        select id from (
            select id, name, dob 
            from   table_a
            minus
            select id, name, dob
            from   table_b)
    );

    commit;

    select * from table_a;

但是,如果在某个时间点,tableA将被重置为与tableB相同,为什么不,如另一个答案所示,截断tableA并从tableB中选择all。

100K并不大。我可以在不到1秒的时间内完成~100K截断并在我的笔记本电脑实例上插入。

答案 5 :(得分:0)

> DELETE FROM purchase WHERE clientcode NOT IN (
>     SELECT clientcode FROM client );

这将从采购表中删除其客户代码不在客户表中的行。购买表的客户代码引用客户表的客户代码。

  

DELETE FROM TABLE1 WHERE FIELD1 NOT IN (SELECT CLIENT1 FROM TABLE2);