【问题标题】:How can i optimize this Update statement?如何优化此更新语句?
【发布时间】:2017-12-01 21:38:45
【问题描述】:

我有以下更新声明

UPDATE CONTRACTSF CF
SET CF.PAYDATE = (SELECT MAX(FLHTEMP.PAYDATE)
                      FROM FLHTEMP
                      WHERE
                      FLHTEMP.FLHDATE = CF.FLHDATE
                      AND FLHTEMP.FLHCOD = CF.FLHCOD
                      AND FLHTEMP.FLHUN = CF.FLHUN
                      AND FLHTEMP.FLHCONTRACT = CF.FLHCONTRACT)
WHERE CF.FLHUN || CF.FLHCONTRACT || CF.FLHDATE 
        IN (SELECT DISTINCT FT.FLHUN || FT.FLHCONTRACT || FT.FLHDATE 
            FROM FLHTEMP FT)

当 FLHTEMP 表有很多记录(6M 记录)时,需要很多小时才能完成。

我尝试过将 IN 与多列一起使用,而不是将其与 || 连接。但没有区别。

我为所有正在使用的列(FLHUN、FLHCONTRACT、FLHCOD、FLHDATE)创建了一个索引,但 Plan explain 显示它没有被使用。

Plan explain 中显示的昂贵操作是 Hash Join、Sort Unique 和 Table full access

【问题讨论】:

  • SELECT DISTINCT ...IN (...) 表达式没有用处。试试WHERE (CF.FLHUN, CF.FLHCONTRACT, CF.FLHDATE) IN (SELECT FT.FLHUN, FT.FLHCONTRACT, FT.FLHDATE FROM ...)
  • 我已经试过了,但性能是一样的
  • 如果没有其他工作(索引等),那么我建议您在两个表中添加一个新列,该列将存储此连接值CF.FLHUN || CF.FLHCONTRACT || CF.FLHDATE。尝试先将该列添加到一个表中,如果仍然没有显着改进,则将其添加到另一个表中。基本上,这将节省 Oracle 进行连接,然后将其存储在某处以在两个表上进行比较。

标签: sql oracle


【解决方案1】:

查询的性能取决于多种因素。比如

  • 表的数据量
  • 连接列的访问类型
  • 表访问类型(全扫描/索引扫描/..)

在不知道表结构和解释计划的情况下很难优化查询。

这里的WHERE子句是不必要的,如果你不想更新三列不匹配的记录那么

  1. 尝试更新 CONTRACTSF 的所有行:NVL 将保留后面加入条件不匹配的值。

    UPDATE CONTRACTSF CF SET CF.PAYDATE = (SELECT NVL(MAX(FLHTEMP.PAYDATE),CF.PAYDATE) FROM FLHTEMP WHERE FLHTEMP.FLHDATE = CF.FLHDATE AND FLHTEMP.FLHCOD = CF.FLHCOD AND FLHTEMP.FLHUN = CF.FLHUN AND FLHTEMP.FLHCONTRACT = CF.FLHCONTRACT)

  2. 或使用 EXISTS 代替 IN

    UPDATE CONTRACTSF CF SET CF.PAYDATE = (SELECT MAX (FLHTEMP.PAYDATE) FROM FLHTEMP WHERE FLHTEMP.FLHDATE = CF.FLHDATE AND FLHTEMP.FLHCOD = CF.FLHCOD AND FLHTEMP.FLHUN = CF.FLHUN AND FLHTEMP.FLHCONTRACT = CF.FLHCONTRACT) WHERE EXISTS (SELECT 1 FROM FLHTEMP FT WHERE FLHTEMP.FLHDATE = CF.FLHDATE AND FLHTEMP.FLHUN = CF.FLHUN AND FLHTEMP.FLHCONTRACT = CF.FLHCONTRACT)

【讨论】:

  • @avjr 除了这个答案,我会在 FLHTEMP (FLHDATE, FLHCOD, FLHUN, FLHCONTRACT, PAYDATE) 上创建一个索引 - 这样所有的列子查询在索引中(您在索引中错过了 PAYDATE)。
  • @avjr 第一条语句的行为与原始查询的行为不完全相同,请参阅提议 MERGE 的答案评论中的解释。选项2很好
【解决方案2】:

试试这个。它需要对 flhemp 进行一次扫描,一次聚合,然后进行一次哈希连接。

merge
 into contractsf cf
using (select flhdate, flhcod, flhun, flhcontract
             ,max(flhtemp.paydate) as max_paydate
         from flhtemp
        group by flhdate, flhcod, flhun, flhcontract
      ) fl
    on(    cf.flhdate     = fl.flhdate
       and cf.flhcod      = fl.flhcod
       and cf.flhun       = fl.flhun
       and cf.flhcontract = fl.flhcontract
      )    
when matched then
   update
      set cf.paydate = fl.max_paydate;

【讨论】:

  • 这提供了最佳执行计划,但不幸的是其行为与原始查询不完全相同。如果两个表在原始语句的 WHERE 中使用的三个列上匹配,但在第四个中不匹配,则 PAYDATE 更新为 NULL。 MERGE 不会改变这样的一行。
猜你喜欢
  • 1970-01-01
  • 2012-03-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多