【问题标题】:Merge Statement is causing performance issues in oracle合并语句在 oracle 中导致性能问题
【发布时间】:2021-08-09 16:16:51
【问题描述】:

我正在尝试在 oracle 中使用合并语句进行更新,源有 20M 记录,目标有 118M 记录。合并的性能需要很长时间。下面列出的是我的合并声明。我还放了解释计划的截图供参考。

MERGE
/*+ parallel(A,10) enable_parallel_dml*/
INTO
(
  SELECT
    PAY_RANGE_START_DATE_KEY,
    AA_PERSON_NATURAL_KEY,
    AA_PERSON_ASSIGNMENT_KEY,
    SCHEDULE_LINE_ID,
    SRC_CREATED_DATE,
    SRC_LAST_UPDATE_DATE
  FROM
    EDWFIN.PSP_LABOR_SCHD_DAY_F_ROLLUP
)
A USING
(
  SELECT
    pay_range_start_date_key,
    aa_person_natural_key,
    aa_person_assignment_key,
    schedule_line_id,
    MAX(src_created_date) src_created_date,
    MAX(src_last_update_date) src_last_update_date
  FROM
    edwfin.psp_labor_schd_day_f_rollup_frs_356
  GROUP BY
    pay_range_start_date_key,
    aa_person_natural_key,
    aa_person_assignment_key,
    schedule_line_id
)
B ON
(
  A.PAY_RANGE_START_DATE_KEY = B.PAY_RANGE_START_DATE_KEY AND
  A.AA_PERSON_NATURAL_KEY    = B.AA_PERSON_NATURAL_KEY AND
  A.AA_PERSON_ASSIGNMENT_KEY = B.AA_PERSON_ASSIGNMENT_KEY AND
  A.SCHEDULE_LINE_ID         = B.SCHEDULE_LINE_ID
)
WHEN MATCHED THEN
  UPDATE
  SET
    A.SRC_CREATED_DATE     = B.SRC_CREATED_DATE,
    A.SRC_LAST_UPDATE_DATE = B.SRC_LAST_UPDATE_DATE
  WHERE
    A.SRC_CREATED_DATE      <> B.SRC_CREATED_DATE
  OR A.SRC_LAST_UPDATE_DATE <> B.SRC_LAST_UPDATE_DATE;
  COMMIT; 

【问题讨论】:

  • 您使用的是 MySQL 还是 Oracle?
  • @jarlh 我正在使用 oracle
  • 这是一次性交易还是您经常需要执行的查询?由于它是单个查询,所有更新都作为一个事务执行,这会减慢整个过程。如果我必须这样做,我会使用一些代码来完成并提交每 1000 次左右的更新。
  • @Tarik 不,它只是一次更新
  • 具有多个提交的循环迭代,尤其是使用外部脚本,将总是比单个 SQL 事务花费更长的时间,可能要长几个数量级.这将强制执行多个 CPU 上下文切换、网络 i/o(甚至在 localhost 上)、重做日志同步操作和控制文件更新,否则这些操作将不存在。如果您将其分解,您还将在事务中失去任何逻辑一致性(如果源数据在中间过程中发生更改会发生什么?)。我在您的解释计划中看不到任何明显的调整机会,除非您将 B 设为物化视图。

标签: sql oracle oracle11g oracle10g sql-merge


【解决方案1】:

如果这是一次性交易,我会简单地使用 CTAS 或 Insert... 作为 select... 重新创建表,然后再添加索引。您已经在使用并行执行; CTAS 或 Insert ... as select... 可以直接做路径。

select 语句看起来像这样。我还假设所有键列都定义为 NOT NULL。

select < all columns from A, except SRC_CREATED_DATE and SRC_LAST_UPDATE_DATE >
    , case 
        when B.PAY_RANGE_START_DATE_KEY is null 
          then A.SRC_CREATED_DATE
          else 
            case
              when     A.SRC_CREATED_DATE <> B.SRC_CREATED_DATE OR A.SRC_LAST_UPDATE_DATE <> B.SRC_LAST_UPDATE_DATE 
                then B.SRC_CREATED_DATE
                else A.SRC_CREATED_DATE
              end
        end SRC_CREATED_DATE
    , case 
        when B.PAY_RANGE_START_DATE_KEY is null 
          then A.SRC_LAST_UPDATE_DATE
          else 
            case
              when     A.SRC_CREATED_DATE <> B.SRC_CREATED_DATE OR A.SRC_LAST_UPDATE_DATE <> B.SRC_LAST_UPDATE_DATE 
                then B.SRC_LAST_UPDATE_DATE
                else A.SRC_LAST_UPDATE_DATE
              end
        end SRC_LAST_UPDATE_DATE
     from EDWFIN.PSP_LABOR_SCHD_DAY_F_ROLLUP  A  
left join (  SELECT
    pay_range_start_date_key,
    aa_person_natural_key,
    aa_person_assignment_key,
    schedule_line_id,
    MAX(src_created_date) src_created_date,
    MAX(src_last_update_date) src_last_update_date
  FROM
    edwfin.psp_labor_schd_day_f_rollup_frs_356
  GROUP BY
    pay_range_start_date_key,
    aa_person_natural_key,
    aa_person_assignment_key,
    schedule_line_id
    ) B
on   A.PAY_RANGE_START_DATE_KEY = B.PAY_RANGE_START_DATE_KEY AND
     A.AA_PERSON_NATURAL_KEY    = B.AA_PERSON_NATURAL_KEY AND
     A.AA_PERSON_ASSIGNMENT_KEY = B.AA_PERSON_ASSIGNMENT_KEY AND
     A.SCHEDULE_LINE_ID         = B.SCHEDULE_LINE_ID

【讨论】:

  • 但我只需要更新 2 列 SRC_CREATED_DATE 和 SRC_LAST_UPDATE_DATE
  • 您能否就上述逻辑如何做到这一点提出建议。
  • 这个概念是您“生成”数据,就像它在更新后的样子一样。这个概念是您使用连接键在 A 和 B 之间进行左外连接。然后,您可以在投影的 CASE 语句中使用原始匹配条件,从 A 或 B 中选择列。
  • 一个小示例代码将为我创造一个美好的世界,是否可以使用上面的代码并给我一个示例
  • 我会用一个简单的例子来编辑我上面的答案,因为我可以很好地格式化它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-05-11
  • 2020-06-27
  • 2015-02-16
  • 2013-11-08
  • 1970-01-01
  • 2010-09-11
  • 2023-03-07
相关资源
最近更新 更多