【问题标题】:Oracle Query optimization for huge table for report用于报表的大表的 Oracle 查询优化
【发布时间】:2019-01-15 05:49:05
【问题描述】:

我正在尝试获取以下查询的输出。它需要永远并且没有输出。你能帮忙优化查询吗?我尝试使用提示并行但仍然没有输出。 TabA 有大约 120 万条记录,TabB 有 70 万条记录。数据库是 Oracle。

WITH TAB1 as (
SELECT
P_TXN_ID,PROD_CD,P_TYPE1,P_TYPE2,
(SELECT COALESCE(FT.V_PARTY_ID_TYPE,'XX') FROM TabA FT WHERE FT.P_TXN_ID=ST.P_TXN_ID AND FT.P_ROLE='FBO' AND LAST_UPD_DT='20-NOV-17' ) FBO,
(SELECT COALESCE(FT.V_PARTY_ID_TYPE,'XX') FROM TabA FT WHERE FT.P_TXN_ID=ST.P_TXN_ID AND FT.P_ROLE='ORG' AND LAST_UPD_DT='20-NOV-17' ) ORG,
(SELECT COALESCE(FT.V_PARTY_ID_TYPE,'XX') FROM TabA FT WHERE FT.P_TXN_ID=ST.P_TXN_ID AND FT.P_ROLE='SEND' AND LAST_UPD_DT='20-NOV-17' )  SEND,
(SELECT COALESCE(FT.V_PARTY_ID_TYPE,'XX') FROM TabA FT WHERE FT.P_TXN_ID=ST.P_TXN_ID AND FT.P_ROLE='RCV' AND LAST_UPD_DT='20-NOV-17' ) RCV,
(SELECT COALESCE(FT.V_PARTY_ID_TYPE,'XX') FROM TabA FT WHERE FT.P_TXN_ID=ST.P_TXN_ID AND FT.P_ROLE='SCND' AND LAST_UPD_DT='20-NOV-17' ) SCND,
(SELECT COALESCE(FT.V_PARTY_ID_TYPE,'XX') FROM TabA FT WHERE FT.P_TXN_ID=ST.P_TXN_ID AND FT.P_ROLE='BENE' AND LAST_UPD_DT='20-NOV-17' ) BENE,
(SELECT COALESCE(FT.V_PARTY_ID_TYPE,'XX') FROM TabA FT WHERE FT.P_TXN_ID=ST.P_TXN_ID AND FT.P_ROLE='INT1' AND LAST_UPD_DT='20-NOV-17' ) INT1,
(SELECT COALESCE(FT.V_PARTY_ID_TYPE,'XX') FROM TabA FT WHERE FT.P_TXN_ID=ST.P_TXN_ID AND FT.P_ROLE='INT2' AND LAST_UPD_DT='20-NOV-17' ) INT2,
(SELECT COALESCE(FT.V_PARTY_ID_TYPE,'XX') FROM TabA FT WHERE FT.P_TXN_ID=ST.P_TXN_ID AND FT.P_ROLE='INT3' AND LAST_UPD_DT='20-NOV-17' ) INT3,
(SELECT COALESCE(FT.V_PARTY_ID_TYPE,'XX') FROM TabA FT WHERE FT.P_TXN_ID=ST.P_TXN_ID AND FT.P_ROLE='INT4' AND LAST_UPD_DT='20-NOV-17' ) INT4,
(SELECT COALESCE(FT.V_PARTY_ID_TYPE,'XX') FROM TabA FT WHERE FT.P_TXN_ID=ST.P_TXN_ID AND FT.P_ROLE='INT5' AND LAST_UPD_DT='20-NOV-17' ) INT5
FROM 
(SELECT PROD_CD,P_TYPE1,P_TYPE2,LAST_UPD_DT,P_TXN_ID 
FROM TabB
) ST
WHERE LAST_UPD_DT>='20-NOV-17' AND LAST_UPD_DT<='22-NOV-17'  
)

SELECT /*+ parallel(100)*/
COUNT(*),
PROD_CD,
P_TYPE1,
FBO,ORG,SEND,RCV,SCND,BENE,
INT1,INT2,INT3,INT4,INT5
FROM 
TAB1 
GROUP BY PROD_CD,
P_TYPE1,
FBO,ORG,SEND,RCV,SCND,BENE,
INT1,INT2,INT3,INT4,INT5
ORDER BY PROD_CD;

提前致谢。只是为了让事情变得简单,下面是上面查询试图实现的示例。假设有表TXN 有以下数据:

TXN
Col1 Col2 Col3 Type
1    AA    abc  FBO
2    null  abc  FBO
3    BB    abc  ORG
4    CC    def  ORG
5    DD    def  ORG
6    EE    pqr  SCND
7    EE    pqr  SCND
8    CC    def  RCV

那么查询的输出将是:

 Col2   Col3 FBO ORG SCND RCV
 AA     abc    1   0    0   0
 null   abc    1   0    0   0
 BB     abc    1   0    0   0
 CC     def    0   1    0   1
 DD     def    0   1    0   0
 EE     pqr    0   0    2   0

【问题讨论】:

  • 检查执行计划了吗?在 with 子句的 select 中有很多子选择。相关列是否已编入索引?您正在将日期列(至少它们看起来像日期类型)与字符串进行比较。这会导致隐式类型转换。你不想要那个。与日期列进行比较时使用 TO_DATE()。
  • 感谢您的评论,我对 DB 无能为力。我正在寻找优化查询
  • 使用EXPLAIN PLAN获取执行计划。
  • 发布执行计划。请参阅here 一些提示如何生成它。

标签: sql oracle performance


【解决方案1】:

我发现很难遵循您的代码和解释。不过,我很确定您只需要条件聚合。

类似这样的:

SELECT b.PROD_CD, b.P_TYPE1, b.P_TYPE2,
       MAX(CASE WHEN FT.P_ROLE = 'FBO' THEN a.V_PARTY_ID_TYPE END) as FBO,
       MAX(CASE WHEN FT.P_ROLE = 'ORG' THEN a.V_PARTY_ID_TYPE END) as ORG,
       MAX(CASE WHEN FT.P_ROLE = 'SEND' THEN a.V_PARTY_ID_TYPE END) as SEND,
       MAX(CASE WHEN FT.P_ROLE = 'RCV' THEN a.V_PARTY_ID_TYPE END) as RCV,
       MAX(CASE WHEN FT.P_ROLE = 'SCND' THEN a.V_PARTY_ID_TYPE END) as SCND,
       MAX(CASE WHEN FT.P_ROLE = 'BENE' THEN a.V_PARTY_ID_TYPE END) as BENE,
       MAX(CASE WHEN FT.P_ROLE = 'FBO' THEN a.V_PARTY_ID_TYPE END) as FBO,
       MAX(CASE WHEN FT.P_ROLE = 'INT1' THEN a.V_PARTY_ID_TYPE END) as INT1,
       MAX(CASE WHEN FT.P_ROLE = 'INT2' THEN a.V_PARTY_ID_TYPE END) as INT2,
       MAX(CASE WHEN FT.P_ROLE = 'INT3' THEN a.V_PARTY_ID_TYPE END) as INT3,
       MAX(CASE WHEN FT.P_ROLE = 'INT4' THEN a.V_PARTY_ID_TYPE END) as INT4
       MAX(CASE WHEN FT.P_ROLE = 'INT5' THEN a.V_PARTY_ID_TYPE END) as INT5
FROM TabB b JOIN
     TabA a
      ON a.P_TXN_ID = b.P_TXN_ID AND
         a.LAST_UPD_DT = DATE '2017-11-20'
WHERE LAST_UPD_DT >= DATE '2017-11-20' AND LAST_UPD_DT <= DATE '2017-11-22'
GROUP BY PROD_CD, P_TYPE1, P_TYPE2;

【讨论】:

  • 好点,这将消除所有可能的索引问题,使两个表的哈希连接。
【解决方案2】:
  1. 如果 tabB 由 LAST_UPD_DT 分区或由 LAST_UPD_DT 索引,请确保 '20-NOV-17' 和 '22-NOV-17' 是日期。为此,请使用 to_date: to_date('22-NOV-2017','dd-mon-yyyy') 等。(可能出于对这一列的索引的想法,但如果你真的更新记录,你的更新会很慢。)
  2. 不要使用并行 100,而是使用并行 8 或 16。100 太重,可能会损坏数据库。
  3. 您需要通过 PROD_CD 订购吗?为什么?没有它会更快。

【讨论】:

    【解决方案3】:

    您没有发布执行计划,因此只能基于猜测提出建议。

    这里有一个检查清单:

    1) 表 TABA 没有被索引 - 显然导致很多 FULL TABLE SCANs

    2) 表 TABA 的索引不正确 - 例如P_ROLE 上有一个索引,LAST_UPD_DT 没有帮助,因为缺少 P_TXN_ID 或

    只有 P_TXN_ID 列被索引,你必须在匹配角色和日期之前扫描大量的数字或行

    3) 表 TABA 已适当索引,即在 P_TXN_ID、P_ROLE、LAST_UPD_DT 上,但您在 TABB 中处理大量行

    TABB 处理 100K 行的示例,您需要 100K * 11 = 1.1M(您的子查询计数)index access。对于磁盘上有索引的大表,每秒将不会超过 100 行。所以 你最好摆脱它,对你的表执行哈希连接。

    最后但同样重要的是 - 您的并行方法类似于您的轮胎空了并且您全速响应时的情况

    【讨论】:

      【解决方案4】:

      要解决您的示例数据结果,您只需要“条件聚合”,基本上这意味着您的查询count(case when ... then 1 end) 如下:

      SELECT
          col2
        , col3
        , COUNT( CASE WHEN type = 'FBO'  THEN 1 END ) AS FBO
        , COUNT( CASE WHEN type = 'ORG'  THEN 1 END ) AS ORG
        , COUNT( CASE WHEN type = 'SCND' THEN 1 END ) AS SCND
        , COUNT( CASE WHEN type = 'RCV'  THEN 1 END ) AS RCV
      FROM txn
      GROUP BY
          col2
        , col3
      ORDER BY
          col2
        , col3
      

      结果是:

      +----+------+------+-----+-----+------+-----+
      |    | COL2 | COL3 | FBO | ORG | SCND | RCV |
      +----+------+------+-----+-----+------+-----+
      |  1 | AA   | abc  |   1 |   0 |    0 |   0 |
      |  2 | BB   | abc  |   0 |   1 |    0 |   0 |
      |  3 | CC   | def  |   0 |   1 |    0 |   1 |
      |  4 | DD   | def  |   0 |   1 |    0 |   0 |
      |  5 | EE   | pqr  |   0 |   0 |    2 |   0 |
      |  6 | NULL | abc  |   1 |   0 |    0 |   0 |
      +----+------+------+-----+-----+------+-----+
      

      所以你可以这样做:

      SELECT
          b.PROD_CD
        , b.P_TYPE1
        , b.P_TYPE2
        , COUNT( CASE WHEN FT.P_ROLE = 'FBO'  THEN a.V_PARTY_ID_TYPE END ) AS FBO
        , COUNT( CASE WHEN FT.P_ROLE = 'ORG'  THEN a.V_PARTY_ID_TYPE END ) AS ORG
        , COUNT( CASE WHEN FT.P_ROLE = 'SEND' THEN a.V_PARTY_ID_TYPE END ) AS SEND
        , COUNT( CASE WHEN FT.P_ROLE = 'RCV'  THEN a.V_PARTY_ID_TYPE END ) AS RCV
        , COUNT( CASE WHEN FT.P_ROLE = 'SCND' THEN a.V_PARTY_ID_TYPE END ) AS SCND
        , COUNT( CASE WHEN FT.P_ROLE = 'BENE' THEN a.V_PARTY_ID_TYPE END ) AS BENE
        , COUNT( CASE WHEN FT.P_ROLE = 'FBO'  THEN a.V_PARTY_ID_TYPE END ) AS FBO
        , COUNT( CASE WHEN FT.P_ROLE = 'INT1' THEN a.V_PARTY_ID_TYPE END ) AS INT1
        , COUNT( CASE WHEN FT.P_ROLE = 'INT2' THEN a.V_PARTY_ID_TYPE END ) AS INT2
        , COUNT( CASE WHEN FT.P_ROLE = 'INT3' THEN a.V_PARTY_ID_TYPE END ) AS INT3
        , COUNT( CASE WHEN FT.P_ROLE = 'INT4' THEN a.V_PARTY_ID_TYPE END ) AS INT4
        , COUNT( CASE WHEN FT.P_ROLE = 'INT5' THEN a.V_PARTY_ID_TYPE END ) AS INT5
      FROM TabB b
      JOIN TabA a ON a.P_TXN_ID = b.P_TXN_ID
      AND a.LAST_UPD_DT = DATE '2017-11-20'
      WHERE LAST_UPD_DT >= DATE '2017-11-20' AND LAST_UPD_DT <= DATE '2017-11-22'
      GROUP BY
          PROD_CD
        , P_TYPE1
        , P_TYPE2;
      

      但请注意,如果 a.V_PARTY_ID_TYPE 为 NULL,则计数不会增加 1。

      【讨论】:

        猜你喜欢
        • 2011-10-15
        • 1970-01-01
        • 1970-01-01
        • 2017-12-25
        • 2016-04-14
        • 1970-01-01
        • 1970-01-01
        • 2010-09-11
        • 1970-01-01
        相关资源
        最近更新 更多