大批量数据删除语句的优化

一、场景

有个离奇的场景,Oracle数据库有两张千万级数据量的表A、B。A和B的对应关系为一对多,从A表中删除主键ID不在B表中的数据。

二、SQL优化

  1. 原删除语句

    DELETE FROM A A WHERE A.ID NOT IN (
        SELECT T.A_ID FROM B T WHERE T.FLAG = '1'
    );
    
  2. 调整两种删除语句,对比执行计划

    1. 第一种

      由于B表中的存储外键,所以有重复的情况,所以这里对not in里面的值的数据量进行去重,减少后约有63000多外键。

      DELETE FROM A A WHERE A.ID NOT IN (
      	SELECT DISTINCT T.A_ID FROM B T WHERE T.FLAG = '1' AND T.A_ID IS NOT NULL
      )
      
    2. 第二种

      not in意味着每条数据都要进行6万多次的对比,这里使用not exists,每条数据进行一次子查询判断是否应该被删除。

      DELETE FROM A A WHERE NOT EXISTS (
      SELECT 1 FROM B T WHERE T.FLAG = '1' AND T.A_ID = A.ID
      )
      

    ​ 但是很可惜,B表中的外键上并没有索引(且外力不允许在这个表上建索引),这样从执行计划上看上面两种语句的效果是一样的。第一种每条数据多次循环判断,第二种每条数据多次IO读取。在测试删除的过程中都会卡死。。。。

  3. 对sql语句进行了再优化

    既然没有索引可用,那就用视图吧缓存一下,想要精确定位到要删除的数据,所以将sql改为这样了

    DELETE FROM A T WHERE T.ID = (
        SELECT A.ID FROM A A
        LEFT JOIN B B ON A.ID = B.A_ID AND B.FLAG = '1'
        WHERE B.A_ID IS NULL AND A.ID = T.ID
    ) 
    

    更改后的sql,从执行计划上看已经有非常大的改观了。

    可是实际跑起来就是无尽的等待。。。没法了给后面加上AND ROWNUM < 1000000后,基本上两分钟内会删除结束,赶紧commit。

三、换个思路

直接上SQL:

CREATE TABLE A_BAK AS 
SELECT A.* FROM A A JOIN B B ON A.ID = B.A_ID AND B.FLAG = '1'

这么多数据要删除,这么难搞,还是重新建表吧。这样A、B两个表只需要全表扫描一次,拿到所有不需要删除的数据放到一张新表中。

上一篇:C++用new操作符申请内存空间


下一篇:Faster R-CNN译文