4

请注意,为了便于阅读,我已经修改了表/字段名称等。一些原始名称相当混乱。

我有三个不同的表:

Retailer (Id+Code is a unique key)
- Id
- Code
- LastReturnDate
- ...

Delivery/DeliveryHistory (combination of Date+RetailerId is unique)
- Date
- RetailerId
- HasReturns
- ...

Delivery并且DeliveryHistory几乎相同。数据会定期移动到历史记录表中,并且没有万无一失的方法可以知道最后一次发生的时间。一般来说,Delivery-table 非常小——通常少于 100,000 行——而历史表通常会有数百万行。

我的任务是根据 DeliveryDeliveryHistory 中为 trueLastReturnDate的当前最高日期值更新每个零售商的字段。HasReturns

以前,这已通过定义如下的视图解决:

SELECT Id, Code, MAX(Date) Date
   FROM Delivery
   WHERE HasReturns = 1
   GROUP BY Id, Code
UNION
SELECT Id, Code, MAX(Date) Date
   FROM DeliveryHistory
   WHERE HasReturns = 1
   GROUP BY Id, Code

以及以下 UPDATE 语句:

UPDATE Retailer SET LastReturnDate = (
    SELECT MAX(Date) FROM DeliveryView
    WHERE Retailer.Id = DeliveryView.Id AND Retailer.Code = DeliveryView.Code)
WHERE Code = :Code AND EXISTS (
    SELECT * FROM DeliveryView
    WHERE Retailer.Id = DeliveryView.Id AND Retailer.Code = DeliveryView.Code
    HAVING
        MAX(Date) > LastReturnDate OR
        (LastReturnDate IS NULL AND MAX(Date) IS NOT NULL))

EXISTS 子句防止更新当前值大于新值的字段,但这实际上不是一个重要的问题,因为很难看出在正常程序执行期间如何发生这种情况。还要注意这AND Max(Date) IS NOT NULL部分实际上是多余的,因为在 DeliveryView 中 Date 不可能为空。但 EXISTS 子句似乎实际上略微提高了性能。

但是,UPDATE 的性能最近非常糟糕。在 Retailer 表仅包含 1000-2000 个相关条目的数据库中,UPDATE 已花费超过五分钟的时间运行。请注意,即使我删除了整个 EXISTS 子句,它也会这样做,即使用这个非常简单的语句:

UPDATE Retailer SET LastReturnDate = (
    SELECT MAX(Date) FROM DeliveryView
    WHERE Retailer.Id = DeliveryView.Id AND Retailer.Code = DeliveryView.Code)
WHERE Code = :Code

因此,我一直在寻找更好的解决方案。我的第一个想法是创建一个临时表,但过了一会儿我尝试将其写为 MERGE 语句:

MERGE INTO Retailer
USING (SELECT Id, Code, MAX(Date) Date FROM DeliveryView GROUP BY Id, Code)
    ON (Retailer.Id = DeliveryView.Id AND Retailer.Code = DeliveryView.Code)
WHEN MATCHED THEN
    UPDATE SET LastReturnDate = Date WHERE Code = :Code

这似乎有效,而且比 UPDATE 快一个数量级以上。

我有三个问题

  1. 我能否确定这在所有情况下都与 UPDATE 具有相同的效果(忽略 LastReturnDate 已经大于 MAX(Date) 的边缘情况)?
  2. 为什么速度这么快?
  3. 有更好的解决方案吗?

查询计划

合并计划

成本:25,831,字节:1,143,828

平白的语言

  1. 读取表 SCHEMA.Delivery 中的每一行。
  2. 行被排序以便被分组。
  3. 读取表 SCHEMA.DeliveryHistory 中的每一行。
  4. 行被排序以便被分组。
  5. 返回步骤 2、4 中的所有行 - 包括重复的行。
  6. 对第 5 步中的行进行排序以消除重复行。
  7. 从存储的视图 SCHEMA.DeliveryView 或步骤 6 中定义的视图定义已处理。
  8. 行被排序以便被分组。
  9. 从存储的视图 SCHEMA 处理视图定义。或如步骤 8 所定义。
  10. 读取表 SCHEMA.Retailer 中的每一行。
  11. 步骤 9、10 的结果集被连接(散列)。
  12. 从存储的视图 SCHEMA 处理视图定义。或由步骤 11 定义。
  13. 行已合并。
  14. 行被远程合并。

技术的

Plan                                            Cardinality Distribution
14 MERGE STATEMENT REMOTE  ALL_ROWS
Cost: 25 831  Bytes: 1 143 828                                              3 738   
    13 MERGE SCHEMA.Retailer ORCL                                           
        12 VIEW SCHEMA.                                         
            11 HASH JOIN  
            Cost: 25 831  Bytes: 1 192 422                                  3 738   
                9 VIEW SCHEMA. 
                Cost: 25 803  Bytes: 194 350                            7 475   
                    8 SORT GROUP BY  
                    Cost: 25 803  Bytes: 194 350                        7 475   
                        7 VIEW VIEW SCHEMA.DeliveryView ORCL
                        Cost: 25 802  Bytes: 194 350                    7 475   
                            6 SORT UNIQUE  
                            Cost: 25 802  Bytes: 134 550                7 475   
                                5 UNION-ALL                 
                                    2 SORT GROUP BY  
                                    Cost: 97  Bytes: 25 362         1 409   
                                        1 TABLE ACCESS FULL TABLE SCHEMA.Delivery [Analyzed] ORCL
                                        Cost: 94  Bytes: 210 654    11 703  
                                    4 SORT GROUP BY  
                                    Cost: 25 705  Bytes: 109 188        6 066   
                                        3 TABLE ACCESS FULL TABLE SCHEMA.DeliveryHistory [Analyzed] ORCL
                                        Cost: 16 827  Bytes: 39 333 636     2 185 202   
                10 TABLE ACCESS FULL TABLE SCHEMA.Retailer [Analyzed] ORCL
                Cost: 27  Bytes: 653 390                            2 230

更新计划

成本:101,492,字节:272,060

平白的语言

  1. 读取表 SCHEMA.Retailer 中的每一行。
  2. 使用索引 SCHEMA.DeliveryHasReturns 检索了一行或多行。索引按升序扫描。
  3. 使用从索引中获取的 rowid 访问表 SCHEMA.Delivery 中的行。
  4. 行被排序以便被分组。
  5. 使用索引 SCHEMA.DeliveryHistoryHasReturns 检索了一行或多行。索引按升序扫描。
  6. 使用从索引获取的 rowid 访问表 SCHEMA.DeliveryHistory 中的行。
  7. 行被排序以便被分组。
  8. 返回步骤 4、7 中的所有行 - 包括重复的行。
  9. 对第 8 步中的行进行排序以消除重复行。
  10. 从存储的视图 SCHEMA.DeliveryView 或步骤 9 中定义的视图定义已处理。
  11. 行被排序以便被分组。
  12. 从存储的视图 SCHEMA 处理视图定义。或由步骤 11 定义。
  13. 行已更新。
  14. 行已远程更新。

技术的

Plan                                        Cardinality Distribution
14 UPDATE STATEMENT REMOTE  ALL_ROWS
Cost: 101 492  Bytes: 272 060                                       1 115   
    13 UPDATE SCHEMA.Retailer ORCL                                      
        1 TABLE ACCESS FULL TABLE SCHEMA.Retailer [Analyzed] ORCL
        Cost: 27  Bytes: 272 060                                1 115   
        12 VIEW SCHEMA. 
        Cost: 90  Bytes: 52                                 2   
            11 SORT GROUP BY  
            Cost: 90  Bytes: 52                             2   
                10 VIEW VIEW SCHEMA.DeliveryView ORCL
                Cost: 90  Bytes: 52                         2   
                    9 SORT UNIQUE  
                    Cost: 90  Bytes: 36                     2   
                        8 UNION-ALL                     
                            4 SORT GROUP BY  
                            Cost: 15  Bytes: 18             1   
                                3 TABLE ACCESS BY INDEX ROWID TABLE SCHEMA.Delivery [Analyzed] ORCL
                                Cost: 14  Bytes: 108        6   
                                    2 INDEX RANGE SCAN INDEX SCHEMA.DeliveryHasReturns [Analyzed] ORCL
                                    Cost: 2     12  
                            7 SORT GROUP BY  
                            Cost: 75  Bytes: 18             1   
                                6 TABLE ACCESS BY INDEX ROWID TABLE SCHEMA.DeliveryHistory [Analyzed] ORCL
                                Cost: 74  Bytes: 4 590          255 
                                    5 INDEX RANGE SCAN INDEX SCHEMA.DeliveryHistoryHasReturns [Analyzed] ORCL
                                    Cost: 6     509
4

0 回答 0