微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

sql – update x set y = null需要很长时间

在工作中,我有一张大桌子(约300万行,像40-50列).我有时需要清空一些列,并填写新的数据.我没想到的是
UPDATE table1 SET y = null

比使用生成的数据(例如,来自同一表的其他列的SQL查询或子查询中的其他表查询的数据)填充列所花费的时间更多.如果我一次通过所有表行(如上面的更新查询),或者如果我使用游标逐行地遍历表(不需要使用pk),这并不重要.如果我在工作中使用大表,或者如果我创建一个小的测试表并填充了大量的测试行,这并不重要.将列设置为null始终会更长(在整个测试中,我遇到的因素是2到10),而不是使用一些动态数据(每行不同)更新列.

这是什么原因?将列设置为null时,Oracle会做什么?或者 – 我的推理错误是什么?

谢谢你的帮助!

P.S .:我正在使用oracle 11g2,并使用plsql开发人员和oracle sql开发人员发现这些结果.

解决方法

概要

我认为更新为null更慢,因为Oracle(不正确地)尝试利用它存储空值的方式,导致它频繁地重新组织块中的行(“堆块压缩”),创建了大量额外的UNDO和重做.

null有什么特别之处?

Oracle Database Concepts

如果空值落在数据值列之间,那么空值将被存储在数据库中,在这些情况下,它们需要1个字节来存储列的长度(零).

一行中的尾随空值不需要存储,因为新的行标题表示上一行中的剩余列为空.例如,如果表的最后三列为空,则不会为这些列存储任何信息.在许多列的表中,
应该最后定义更容易包含空值的列,以节省磁盘空间.

测试

基准更新非常困难,因为更新的真实成本不能仅仅从更新语句来测量.例如,日志切换将会
每次更新都不会发生,延迟块清理将在以后发生.要准确测试更新,应该有多个运行,
应该为每个运行重新创建对象,并且应该丢弃高和低的值.

为了简单起见,下面的脚本不会抛出高低的结果,只能用单列测试表.但问题仍然存在,无论列数,数据和哪些列被更新.

我使用http://www.oracle-developer.net/utilities.php中的RunStats实用程序来比较update-to-a-value与update-to-a-null的资源消耗.

create table test1(col1 number);

BEGIN
    dbms_output.enable(1000000);

   runstats_pkg.rs_start;

    for i in 1 .. 10 loop
        execute immediate 'drop table test1 purge';
        execute immediate 'create table test1 (col1 number)';
        execute immediate 'insert /*+ append */ into test1 select 1 col1
            from dual connect by level <= 100000';
        commit;
        execute immediate 'update test1 set col1 = 1';
        commit;
    end loop;

   runstats_pkg.rs_pause;
   runstats_pkg.rs_resume;

    for i in 1 .. 10 loop
        execute immediate 'drop table test1 purge';
        execute immediate 'create table test1 (col1 number)';
        execute immediate 'insert /*+ append */ into test1 select 1 col1
            from dual connect by level <= 100000';
        commit;
        execute immediate 'update test1 set col1 = null';
        commit;
    end loop;

   runstats_pkg.rs_stop();
END;
/

结果

有几十个差异,这些是我认为最相关的四个:

Type  Name                                 Run1         Run2         Diff
----- ---------------------------- ------------ ------------ ------------
TIMER elapsed time (hsecs)                1,269        4,738        3,469
STAT  heap block compress                     1        2,028        2,027
STAT  undo change vector size        55,855,008  181,387,456  125,532,448
STAT  redo size                     133,260,596  581,641,084  448,380,488

解决方案?

我可以想到的唯一可能的解决方案是启用表压缩.压缩表不会发生尾随空存储技巧.
所以即使“堆块压缩”数量从2028到23208的Run2更高,我猜这实际上并没有做任何事情.
两次运行之间的重做,撤消和已用时间与启用的表压缩几乎相同.

然而,表压缩有很多潜在的缺点.更新到null将运行得更快,但每个其他更新将运行至少稍慢一些.

原文地址:https://www.jb51.cc/mssql/81725.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐