更新32k行的更新声明需要超过24小时

时间:2018-05-15 08:25:37

标签: sql oracle sql-update sql-insert

以下是我正在运行32k次的更新语句,并且运行时间超过15小时。 我必须更新表2中的32k不同M_DISPLAY值的值。

UPDATE TABLE_2 T2  SET  T2.M_VALUE = 'COL_ANC' 
WHERE EXISTS (SELECT 1 FROM TABLE_2 T1  WHERE  TRIM(T1.M_DISPLAY) = 'ANCHORTST' AND T1.M_LABEL=T2.M_LABEL );

我不确定为什么我花了这么长时间调整查询,

我在Update.sql文件中复制了32000个更新语句,并在命令行中运行SQL。 虽然它正在更新表格,但这是一个无休止的过程

如果我在任何地方出错,请提供建议

此致

3 个答案:

答案 0 :(得分:2)

使用FORALL

如果您无法重写查询以运行单个批量更新而不是32k个人更新,那么使用PL / SQL的FORALL仍然可以获得幸运。一个例子:

DECLARE
  TYPE rec_t IS RECORD (
    m_value   table_2.m_value%TYPE,
    m_display table_2.m_display%TYPE
  );

  TYPE tab_t IS TABLE OF rec_t;

  data tab_t := tab_t();
BEGIN

  -- Fill in data object. Replace this by whatever your logic for matching
  -- m_value to m_display is
  data.extend(1);
  data(1).m_value := 'COL_ANC';
  data(1).m_display := 'ANCHORTST';

  -- Then, run the 32k updates using FORALL
  FORALL i IN 1 .. data.COUNT
    UPDATE table_2 t2
    SET t2.m_value = data(i).m_value
    WHERE EXISTS (
      SELECT 1
      FROM table_2 t1
      WHERE trim(t1.m_display) = data(i).m_display
      AND t1.m_label = t2.m_label
    );
END;
/

并发

如果您不是系统中的唯一进程,则单个事务中的32k更新可能会受到影响。绝对值得在子事务中提交几千行,以减少在您更新时可能读取同一个表的其他进程的并发效应。

批量更新

实际上,任何改进的目标都应该是一次性批量更新整个数据集(或者可能分成几个批量,请参见并发)。

如果您有一个包含更新说明的登台表:

CREATE TABLE update_instructions (
  m_value VARCHAR2(..),
  m_display VARCHAR2(..)
);

然后你可以按照以下方式取消:

MERGE INTO table_2 t2
USING (
  SELECT u.*, t1.m_label
  FROM update_instructions u
  JOIN table_2 t1 ON trim(t1.m_display) = u.m_display
) t1
ON t2.m_label = t1.m_label
WHEN MATCHED THEN UPDATE SET t2.m_value = t1.m_value;

这应该比FORALL更快(但可能会有更多的并发影响)。

索引和数据清理

当然,运行32k个别更新语句时可能会对您造成伤害的一件事是TRIM()函数,它会阻止M_DISPLAY上的索引有效使用。如果你可以清理你的数据,所以它不需要先修剪,这肯定会有所帮助。否则,您可以仅为更新添加基于函数的索引(然后再将其删除):

CREATE INDEX i ON table_2 (trim (m_display));

答案 1 :(得分:0)

查询和子查询查询同一个表:TABLE_2。假设M_LABEL是唯一的,子查询为TABLE_2中的所有行返回1,其中M_DISPLAY是ANCHORTST。然后更新查询为子查询返回的所有1更新相同的(!)TABLE_2 - 因此对于M_DISPLAY为ANCHORTST的所有行。

因此,可以简化查询,利用更新和选择工作在同一个表上的事实 - TABLE_2:

UPDATE TABLE_2 T2  SET  T2.M_VALUE = 'COL_ANC' WHERE TRIM(T2.M_DISPLAY) = 'ANCHORTST'

如果M_LABEL不是唯一的,那么以上就不会起作用 - 感谢评论员指出这一点!

答案 2 :(得分:0)

为了显着加快执行速度:

  1. 确保您已在WHERE子句中的M_DISPLAY和M_LABEL列上创建索引。

  2. 确保M_DISPLAY具有基于函数的索引。如果没有,则不要将其传递给TRIM函数,因为该函数将阻止数据库使用您为M_DISPLAY列创建的索引。在存储到表格中之前对数据进行TRIM。

  3. 多数民众赞成。

    顺便说一句,正如已经提到的,您不应该需要32k查询来实现您的目标。一个人可能就够了。查看基于查询的更新。例如,请在此处查看已接受的答案: Oracle SQL: Update a table with data from another table