Oracle Query优化以获取庞大的报表

时间:2019-01-15 05:49:05

标签: sql oracle performance

我正在尝试获取以下查询的输出。它要永远做,没有输出。您能帮忙优化查询吗?我尝试使用提示并行,但仍然没有输出。 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

4 个答案:

答案 0 :(得分: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;

答案 1 :(得分:0)

  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订购吗?为什么?没有它,它将更快。

答案 2 :(得分:0)

您没有发布执行计划,因此只能进行基于猜测的推荐。

以下是检查清单:

1)表TABA未建立索引-显然导致大量FULL TABLE SCAN

2)表TABA的索引编入不正确-例如由于P_TXN_ID丢失或

,P_ROLE LAST_UPD_DT上的索引无济于事

仅索引P_TXN_ID列,并且您必须先扫描大量数字或行,然后才能匹配角色和日期

3)表TABA已正确索引,即在P_TXN_ID,P_ROLE,LAST_UPD_DT上,但您在TABB中处理了大量行

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

最后但并非最不重要的-您采用平行运动的方法类似于轮胎空了并且油门全开的情况。

答案 3 :(得分:0)

要解决要得出的样本数据,您只需要“条件聚合”,基本上这意味着对查询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。