【问题标题】:Query Optimization with ROW_NUMBER使用 ROW_NUMBER 进行查询优化
【发布时间】:2022-01-18 16:06:57
【问题描述】:

我有这个问题:

SELECT 
    PE1.PRODUCT_EQUIPMENT_KEY, -- primary key
    PE1.Customer_Ban, 
    PE1.Subscriber_No, 
    PE1.Prod_Equip_Cd, 
    PE1.Prod_Equip_Txt, 
    PE1.Prod_Equip_Category_Txt--,
    -- PE2.ep_rnk ------------------ UNCOMMENT THIS LINE
FROM 
    INT_ADM.Product_Equipment_Dim PE1
    INNER JOIN 
    ( 
        SELECT 
            PRODUCT_EQUIPMENT_KEY,
            ROW_NUMBER() OVER (PARTITION BY Customer_Ban, Subscriber_No ORDER BY Start_Dt ASC) AS ep_rnk
            FROM INT_ADM.Product_Equipment_Dim PE2
    ) PE2
    ON PE2.PRODUCT_EQUIPMENT_KEY = PE1.PRODUCT_EQUIPMENT_KEY
WHERE 
    Line_Of_Business_Cd = 'M' 
    AND /*v_Date_Start*/ TO_DATE( '2022/01/12', 'yyyy/mm/dd' ) BETWEEN Start_Dt AND End_Dt 
    AND Current_Ind = 'Y' 

如果我按照你看到的那样运行它,那么它会在一秒钟内运行。

如果我在 -- PE2.ep_rnk ------------------ UNCOMMENT THIS LINE 未注释的情况下运行它,则查询最多需要 5 分钟才能完成。

我知道这与 ROW_NUMBER() 有关,但是在网上查遍之后,我找不到很好的解释和解决方案。有谁知道为什么取消注释该行会使查询变得如此缓慢,以及我能做些什么让它运行得快?

非常感谢您提前提供的帮助。

【问题讨论】:

  • 该子查询强制为表中的 每一 行进行昂贵的行号计算,临时缓存响应,然后与数据子集进行昂贵的连接。那是你真正想要的吗?是否需要过滤之前计算行号?否则,您可以将注释行替换为 ROW_NUMBER 表达式
  • 如果您注释该行,则忽略整个子查询,因为它不影响结果 - 子查询在 PK 上执行自联接,不会过滤任何行
  • 执行计划是什么样的?您可以用返回所有必要字段 * 和行号的 CTE 或嵌套查询替换自联接,并使用外部查询过滤结果。查询引擎可能已经这样做了
  • 重要提示 - 有多少行(与总行数相比)返回查询。另请了解如何检查(并发布)这两个查询的execution plan

标签: sql oracle optimization


【解决方案1】:

根本原因是,即使where 子句中的谓词允许有效访问表的行(但我怀疑您的不到一秒 response 是获取结果第一页的时间),您需要在子查询中访问表的所有行,对它们进行窗口排序并最终加入它们到第一行源。

因此,如果您注释掉 ep_rnk Oracle smart enought,它根本不需要评估子查询,因为子查询位于 同一张表并且连接在主键上 - 所以连接中不会丢失或重复任何行。

你可以改进什么?

不多。如果WHERE 条件过滤表非常严格(并且您只以少量PRODUCT_EQUIPMENT_KEY 结束)在子查询中使用相同的过滤器:

( 
    SELECT 
        PRODUCT_EQUIPMENT_KEY,
        ROW_NUMBER() OVER (PARTITION BY Customer_Ban, Subscriber_No ORDER BY Start_Dt ASC) AS ep_rnk
        FROM INT_ADM.Product_Equipment_Dim PE2
        --- filer added
        WHERE PRODUCT_EQUIPMENT_KEY in (
            SELECT PRODUCT_EQUIPMENT_KEY
            FROM INT_ADM.Product_Equipment_Dim
            WHERE ... same predicate as in the main query ...
        )
) PE2

如果谓词返回所有(或大部分)PRODUCT_EQUIPMENT_KEY,唯一(经常使用)的方法是预先计算排名,例如在物化视图中

materialized view 定义如下

SELECT 
    PE1.PRODUCT_EQUIPMENT_KEY, -- primary key
    PE1.Customer_Ban, 
    PE1.Subscriber_No, 
    PE1.Prod_Equip_Cd, 
    PE1.Prod_Equip_Txt, 
    PE1.Prod_Equip_Category_Txt--,
    ROW_NUMBER() OVER (PARTITION BY Customer_Ban, Subscriber_No ORDER BY Start_Dt ASC) AS ep_rnk
FROM 
    INT_ADM.Product_Equipment_Dim PE1

您可以从中进行简单查询 - 无需连接。

【讨论】:

    猜你喜欢
    • 2016-06-08
    • 1970-01-01
    • 1970-01-01
    • 2016-01-06
    • 2016-08-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-14
    相关资源
    最近更新 更多