GaussDB(DWS)性能调优:常量标量子查询做全连接出现NestLoop导致整体慢

举报
Zawami 发表于 2023/11/27 14:19:56 2023/11/27
【摘要】 本篇文章主要探讨和解决一个标量子查询同另一查询做nest loop导致整体性能劣化的问题。

1、【问题描述】

由于SQL中存在标量子查询同另一查询做笛卡尔积使SQL整体慢。标量子查询,即结果集只有一行一列的子查询。这里导致的SQL语句执行慢不只是在于做笛卡尔积慢,也会使后续聚合更慢。


2、【原始语句】

WITH TMP AS(
  SELECT
    case
      when length('[“202309“]') = 6 then '[“202309“]' || '01'
      WHEN length('[“202309“]') <> 8 THEN TO_CHAR(CURRENT_DATE, 'YYYYMMDD')
    END AS V_DATE
  from
    DUAL
)
SELECT
  BG_CODE,
  BG_CN_NAME,
  BG_EN_NAME,
  METRIC_CODE --指标ID
,
  METRIC_CN_NAME --指标中文名称
,
  METRIC_EN_NAME --指标英文名称
,
  CURRENCY --币种
,
  OVERSEAS_FLAG,
  REGION_CODE,
  REGION_CN_NAME,
  REGION_EN_NAME,
  REPOFFICE_CODE,
  REPOFFICE_CN_NAME,
  REPOFFICE_EN_NAME,
  OFFICE_CODE,
  OFFICE_CN_NAME,
  OFFICE_EN_NAME,
  REGION_CUSTCATG_CODE,
  REGION_CUSTCATG_CN_NAME,
  REGION_CUSTCATG_EN_NAME,
  TOP_CUST_CATEGORY_CODE,
  TOP_CUST_CATEGORY_EN_NAME,
  TOP_CUST_CATEGORY_CN_NAME,
  ACCTCUST_HQ_CODE,
  ACCTCUST_HQ_CN_NAME,
  ACCTCUST_HQ_EN_NAME,
  ACCTCUST_BRANCH_CODE,
  ACCTCUST_BRANCH_CN_NAME,
  ACCTCUST_BRANCH_EN_NAME,
  ACCTCUST_SUBSIDIARY_CODE,
  ACCTCUST_SUBSIDIARY_CN_NAM,
  ACCTCUST_SUBSIDIARY_EN_NAM,
  COUNTRY_CODE --新增加入参
,
  COUNTRY_CN_NAME --新增加入参
,
  COUNTRY_EN_NAME --新增加入参
,
  AGREE_AMOUNT --BUSI_DSCT_00001   总优惠
,
  AGREE_REMAIN_AMOUNT --BUSI_DSCT_00002   即期优惠
,
  SIGN_AMOUNT --BUSI_DSCT_00003   即期优惠/一次性优惠
,
  USE_AMOUNT --BUSI_DSCT_00004   即期优惠/单价量折扣
,
  NOT_USED_VALID_AMOUNT --BUSI_DSCT_00005   延期优惠
,
  NOT_USED_INVALID_AMOUNT --BUSI_DSCT_00006   voucher
,
  NEW_SIGN_AMOUNT --BUSI_DSCT_00007   其他延期优惠
,
  NEW_USE_AMOUNT --BUSI_DSCT_00008   本月新使用金额
,
  EXPIRED_AMOUNT --BUSI_DSCT_00009   本月已过期金额
,
  IMMED_EXPIRED_AMOUNT --BUSI_DSCT_00010   半年内即将过期金额
FROM
  (
    SELECT
      C.BG_CODE,
      C.BG_CN_NAME,
      C.BG_EN_NAME,
      C.M_ID AS METRIC_CODE --指标ID
,
      C.M_CN AS METRIC_CN_NAME --指标中文名称
,
      C.M_EN AS METRIC_EN_NAME --指标英文名称
,
      C.CURRENCY_CODE AS CURRENCY --币种
,CASE
        WHEN 1 = 0 THEN C.OVERSEA_FLAG
        ELSE NULL
      END AS OVERSEAS_FLAG,CASE
        WHEN 1 = 0 THEN C.REGION_CODE
        ELSE NULL
      END AS REGION_CODE,CASE
        WHEN 1 = 0 THEN C.REGION_CN_NAME
        ELSE NULL
      END AS REGION_CN_NAME,CASE
        WHEN 1 = 0 THEN C.REGION_EN_NAME
        ELSE NULL
      END AS REGION_EN_NAME,CASE
        WHEN 1 = 0 THEN C.REPOFFICE_CODE
        ELSE NULL
      END AS REPOFFICE_CODE,CASE
        WHEN 1 = 0 THEN C.REPOFFICE_CN_NAME
        ELSE NULL
      END AS REPOFFICE_CN_NAME,CASE
        WHEN 1 = 0 THEN C.REPOFFICE_EN_NAME
        ELSE NULL
      END AS REPOFFICE_EN_NAME,CASE
        WHEN 1 = 0 THEN C.OFFICE_CODE
        ELSE NULL
      END AS OFFICE_CODE,CASE
        WHEN 1 = 0 THEN C.OFFICE_CN_NAME
        ELSE NULL
      END AS OFFICE_CN_NAME,CASE
        WHEN 1 = 0 THEN C.OFFICE_EN_NAME
        ELSE NULL
      END AS OFFICE_EN_NAME,CASE
        WHEN 1 = 0 THEN C.REGION_CUSTCATG_CODE
        ELSE NULL
      END AS REGION_CUSTCATG_CODE,CASE
        WHEN 1 = 0 THEN C.REGION_CUSTCATG_CN_NAME
        ELSE NULL
      END AS REGION_CUSTCATG_CN_NAME,CASE
        WHEN 1 = 0 THEN C.REGION_CUSTCATG_EN_NAME
        ELSE NULL
      END AS REGION_CUSTCATG_EN_NAME,CASE
        WHEN 1 = 0 THEN C.TOP_CUST_CATEGORY_CODE
        ELSE NULL
      END AS TOP_CUST_CATEGORY_CODE,CASE
        WHEN 1 = 0 THEN C.TOP_CUST_CATEGORY_EN_NAME
        ELSE NULL
      END AS TOP_CUST_CATEGORY_EN_NAME,CASE
        WHEN 1 = 0 THEN C.TOP_CUST_CATEGORY_CN_NAME
        ELSE NULL
      END AS TOP_CUST_CATEGORY_CN_NAME,CASE
        WHEN 1 = 0 THEN C.ACCTCUST_HQ_CODE
        ELSE NULL
      END AS ACCTCUST_HQ_CODE,CASE
        WHEN 1 = 0 THEN C.ACCTCUST_HQ_CN_NAME
        ELSE NULL
      END AS ACCTCUST_HQ_CN_NAME,CASE
        WHEN 1 = 0 THEN C.ACCTCUST_HQ_EN_NAME
        ELSE NULL
      END AS ACCTCUST_HQ_EN_NAME,CASE
        WHEN 1 = 0 THEN C.ACCTCUST_BRANCH_CODE
        ELSE NULL
      END AS ACCTCUST_BRANCH_CODE,CASE
        WHEN 1 = 0 THEN C.ACCTCUST_BRANCH_CN_NAME
        ELSE NULL
      END AS ACCTCUST_BRANCH_CN_NAME,CASE
        WHEN 1 = 0 THEN C.ACCTCUST_BRANCH_EN_NAME
        ELSE NULL
      END AS ACCTCUST_BRANCH_EN_NAME,CASE
        WHEN 1 = 0 THEN C.ACCTCUST_SUBSIDIARY_CODE
        ELSE NULL
      END AS ACCTCUST_SUBSIDIARY_CODE,CASE
        WHEN 1 = 0 THEN C.ACCTCUST_SUBSIDIARY_CN_NAM
        ELSE NULL
      END AS ACCTCUST_SUBSIDIARY_CN_NAM,CASE
        WHEN 1 = 0 THEN C.ACCTCUST_SUBSIDIARY_EN_NAM
        ELSE NULL
      END AS ACCTCUST_SUBSIDIARY_EN_NAM,CASE
        WHEN 1 = 0 THEN C.COUNTRY_CODE
        ELSE NULL
      END AS COUNTRY_CODE --新增加入参
,CASE
        WHEN 1 = 0 THEN C.COUNTRY_CN_NAME
        ELSE NULL
      END AS COUNTRY_CN_NAME --新增加入参
,CASE
        WHEN 1 = 0 THEN C.COUNTRY_EN_NAME
        ELSE NULL
      END AS COUNTRY_EN_NAME --新增加入参
,
      SUM(C.AGREE_AMOUNT) AS AGREE_AMOUNT --协议金额
,
      SUM(C.AGREE_REMAIN_AMOUNT) AS AGREE_REMAIN_AMOUNT --协议剩余金额
,
      SUM(C.SIGN_AMOUNT) AS SIGN_AMOUNT --可用金额
,
      SUM(C.USE_AMOUNT) AS USE_AMOUNT --已使用金额
,
      SUM(
        CASE
          WHEN C.DSCT_TYPE = 'VOUCHER'
          AND NVL(
            C.EXPIRED_DATE,
            add_months(C.EFFECTIVE_DATE, C.VALID_MONTH)
          ) >= to_date(T.V_DATE, 'yyyymmdd') THEN C.AGREE_REMAIN_AMOUNT
          WHEN C.DSCT_TYPE in (
            'FOC',
            'Volume Based List Price Adjustment',
            'One-Time Discount'
          )
          AND C.DSCT_END_DATE >= to_date(T.V_DATE, 'yyyymmdd') THEN C.EFFECTIVE_TOTAL_AMOUNT
          ELSE NULL
        END
      ) AS NOT_USED_VALID_AMOUNT --未使用金额(有效期外)
,
      SUM(
        CASE
          WHEN C.DSCT_TYPE = 'VOUCHER' THEN C.SIGN_AMOUNT
          ELSE C.EFFECTIVE_TOTAL_AMOUNT
        END - CASE
          WHEN C.DSCT_TYPE = 'VOUCHER' THEN C.USE_AMOUNT
          ELSE C.USED_TOTAL_AMOUNT
        END - CASE
          WHEN C.DSCT_TYPE = 'VOUCHER'
          AND NVL(
            C.EXPIRED_DATE,
            add_months(C.EFFECTIVE_DATE, C.VALID_MONTH)
          ) >= to_date(T.V_DATE, 'yyyymmdd') THEN C.AGREE_REMAIN_AMOUNT
          WHEN C.DSCT_TYPE in (
            'FOC',
            'Volume Based List Price Adjustment',
            'One-Time Discount'
          )
          AND C.DSCT_END_DATE >= to_date(T.V_DATE, 'yyyymmdd') THEN C.EFFECTIVE_TOTAL_AMOUNT
          ELSE NULL
        END
      ) AS NOT_USED_INVALID_AMOUNT --未使用金额(有效期内)
,
      SUM(
        CASE
          WHEN C.DSCT_TYPE = 'VOUCHER'
          AND C.EXPIRED_DATE >= to_date(substr(T.V_DATE, 1, 6), 'yyyymm')
          and C.EXPIRED_DATE <= LAST_DAY(to_date(T.V_DATE, 'yyyymmdd')) THEN C.SIGN_AMOUNT
          WHEN C.DSCT_TYPE in (
            'FOC',
            'Volume Based List Price Adjustment',
            'One-Time Discount'
          )
          AND C.DSCT_START_DATE >= to_date(substr(T.V_DATE, 1, 6), 'yyyymm')
          and C.DSCT_START_DATE <= LAST_DAY(to_date(T.V_DATE, 'yyyymmdd')) THEN C.EFFECTIVE_TOTAL_AMOUNT
          ELSE NULL
        END
      ) AS NEW_SIGN_AMOUNT --本月新增可用金额
,
      SUM(
        CASE
          WHEN C.DSCT_TYPE = 'VOUCHER'
          AND C.EFFECTIVE_DATE >= to_date(substr(T.V_DATE, 1, 6), 'yyyymm')
          and C.EFFECTIVE_DATE <= LAST_DAY(to_date(T.V_DATE, 'yyyymmdd')) THEN C.USE_AMOUNT
          WHEN C.DSCT_TYPE in (
            'FOC',
            'Volume Based List Price Adjustment',
            'One-Time Discount'
          )
          AND C.DSCT_START_DATE >= to_date(substr(T.V_DATE, 1, 6), 'yyyymm')
          and C.DSCT_START_DATE <= LAST_DAY(to_date(T.V_DATE, 'yyyymmdd')) THEN C.USED_TOTAL_AMOUNT
          ELSE NULL
        END
      ) AS NEW_USE_AMOUNT --本月新使用金额
,
      SUM(
        CASE
          WHEN C.DSCT_TYPE = 'VOUCHER'
          AND C.EXPIRED_DATE < to_date(T.V_DATE, 'yyyymmdd') THEN C.AGREE_REMAIN_AMOUNT
          WHEN C.DSCT_TYPE in (
            'FOC',
            'Volume Based List Price Adjustment',
            'One-Time Discount'
          )
          AND C.DSCT_END_DATE < to_date(T.V_DATE, 'yyyymmdd') THEN C.EFFECTIVE_TOTAL_AMOUNT
          ELSE NULL
        END
      ) AS EXPIRED_AMOUNT --本月已过期金额
,
      SUM(
        CASE
          WHEN C.DSCT_TYPE = 'VOUCHER'
          AND C.EXPIRED_DATE BETWEEN to_date(T.V_DATE, 'yyyymmdd')
          AND add_months(to_date(T.V_DATE, 'yyyymmdd'), 6) THEN C.AGREE_REMAIN_AMOUNT
          WHEN C.DSCT_TYPE in (
            'FOC',
            'Volume Based List Price Adjustment',
            'One-Time Discount'
          )
          AND C.DSCT_END_DATE BETWEEN to_date(T.V_DATE, 'yyyymmdd')
          AND add_months(to_date(T.V_DATE, 'yyyymmdd'), 6) THEN C.EFFECTIVE_TOTAL_AMOUNT
          ELSE NULL
        END
      ) AS IMMED_EXPIRED_AMOUNT --半年内即将过期金额
    FROM
      DMSALESW.DM_SALE_BUSI_DSCT_SUM_F C
      LEFT JOIN TMP T ON 1 = 1
    WHERE
      C.CURRENCY_CODE IN ('USD') --改为多值
      AND C.BG_CODE IN ('PDCG901159')
      AND C.M_ID IN (
        'BUSI_DSCT_00001',
        'BUSI_DSCT_00002',
        'BUSI_DSCT_00003',
        'BUSI_DSCT_00004',
        'BUSI_DSCT_00005',
        'BUSI_DSCT_00006',
        'BUSI_DSCT_00007'
      ) --新增加字段
      --AND C.M_CN IN ('#[#P_REPORT_ITEM_NAME#]#') --新增加字段
      --新增加字段
    GROUP BY
      C.BG_CODE,
      C.BG_CN_NAME,
      C.BG_EN_NAME,
      C.M_ID --指标ID
,
      C.M_CN --指标中文名称
,
      C.M_EN --指标英文名称
,
      C.CURRENCY_CODE --币种
,CASE
        WHEN 1 = 0 THEN C.OVERSEA_FLAG
        ELSE NULL
      END,CASE
        WHEN 1 = 0 THEN C.REGION_CODE
        ELSE NULL
      END,CASE
        WHEN 1 = 0 THEN C.REGION_CN_NAME
        ELSE NULL
      END,CASE
        WHEN 1 = 0 THEN C.REGION_EN_NAME
        ELSE NULL
      END,CASE
        WHEN 1 = 0 THEN C.REPOFFICE_CODE
        ELSE NULL
      END,CASE
        WHEN 1 = 0 THEN C.REPOFFICE_CN_NAME
        ELSE NULL
      END,CASE
        WHEN 1 = 0 THEN C.REPOFFICE_EN_NAME
        ELSE NULL
      END,CASE
        WHEN 1 = 0 THEN C.OFFICE_CODE
        ELSE NULL
      END,CASE
        WHEN 1 = 0 THEN C.OFFICE_CN_NAME
        ELSE NULL
      END,CASE
        WHEN 1 = 0 THEN C.OFFICE_EN_NAME
        ELSE NULL
      END,CASE
        WHEN 1 = 0 THEN C.REGION_CUSTCATG_CODE
        ELSE NULL
      END,CASE
        WHEN 1 = 0 THEN C.REGION_CUSTCATG_CN_NAME
        ELSE NULL
      END,CASE
        WHEN 1 = 0 THEN C.REGION_CUSTCATG_EN_NAME
        ELSE NULL
      END,CASE
        WHEN 1 = 0 THEN C.TOP_CUST_CATEGORY_CODE
        ELSE NULL
      END,CASE
        WHEN 1 = 0 THEN C.TOP_CUST_CATEGORY_EN_NAME
        ELSE NULL
      END,CASE
        WHEN 1 = 0 THEN C.TOP_CUST_CATEGORY_CN_NAME
        ELSE NULL
      END,CASE
        WHEN 1 = 0 THEN C.ACCTCUST_HQ_CODE
        ELSE NULL
      END,CASE
        WHEN 1 = 0 THEN C.ACCTCUST_HQ_CN_NAME
        ELSE NULL
      END,CASE
        WHEN 1 = 0 THEN C.ACCTCUST_HQ_EN_NAME
        ELSE NULL
      END,CASE
        WHEN 1 = 0 THEN C.ACCTCUST_BRANCH_CODE
        ELSE NULL
      END,CASE
        WHEN 1 = 0 THEN C.ACCTCUST_BRANCH_CN_NAME
        ELSE NULL
      END,CASE
        WHEN 1 = 0 THEN C.ACCTCUST_BRANCH_EN_NAME
        ELSE NULL
      END,CASE
        WHEN 1 = 0 THEN C.ACCTCUST_SUBSIDIARY_CODE
        ELSE NULL
      END,CASE
        WHEN 1 = 0 THEN C.ACCTCUST_SUBSIDIARY_CN_NAM
        ELSE NULL
      END,CASE
        WHEN 1 = 0 THEN C.ACCTCUST_SUBSIDIARY_EN_NAM
        ELSE NULL
      END,CASE
        WHEN 1 = 0 THEN C.COUNTRY_CODE
        ELSE NULL
      END --新增加入参
,CASE
        WHEN 1 = 0 THEN C.COUNTRY_CN_NAME
        ELSE NULL
      END --新增加入参
,CASE
        WHEN 1 = 0 THEN C.COUNTRY_EN_NAME
        ELSE NULL
      END
  ) T --新增加入参

3、【原因分析】

从SQL中可以看到TMP为标量子查询,并且在子查询T中和物理表C做了笛卡尔积。 下面是该SQL的部分执行计划(全量执行计划在附件中给出):

 id |                                operation                                |        A-time        | A-rows  | E-rows  | E-distinct |  Peak Memory   | E-memory |  A-width  | E-width |  E-costs  
----+-------------------------------------------------------------------------+----------------------+---------+---------+------------+----------------+----------+-----------+---------+-----------
  1 | ->  Row Adapter                                                         | 3037.648             |       7 |     245 |            | 419KB          |          |           |    1318 | 117210.62 
  2 |    ->  Vector Streaming (type: GATHER)                                  | 3037.633             |       7 |     245 |            | 777KB          |          |           |    1318 | 117210.62 
  3 |       ->  Vector Hash Aggregate                                         | [3031.872, 3032.516] |       7 |     245 |            | [4MB, 4MB]     | 16MB     | [0,870]   |     557 | 117128.41 
  4 |          ->  Vector Streaming(type: REDISTRIBUTE)                       | [3031.560, 3032.232] |     112 |    3920 |            | [1MB, 1MB]     | 2MB      |           |     557 | 116852.33 
  5 |             ->  Vector Hash Aggregate                                   | [2728.059, 2909.255] |     112 |    3920 |            | [8MB, 8MB]     | 16MB     | [833,833] |     557 | 116699.48 
  6 |                ->  Vector Nest Loop Left Join (7, 8)                    | [441.050, 471.725]   | 3007901 | 2106919 |            | [1MB, 1MB]     | 1MB      |           |     237 | 67316.28  
  7 |                   ->  CStore Scan on dmsalesw.dm_sale_busi_dsct_sum_f c | [145.354, 158.560]   | 3007901 | 2106919 |            | [5MB, 5MB]     | 1MB      |           |     205 | 65011.82  
  8 |                   ->  Vector Materialize                                | [32.034, 38.902]     | 3007901 |       1 |            | [288KB, 288KB] | 16MB     | [21,21]   |      32 | 0.03      
  9 |                      ->  Vector Subquery Scan on dual                   | [0.067, 0.093]       |      16 |       1 |            | [128KB, 128KB] | 1MB      |           |      32 | 0.02      
 10 |                         ->  Vector Adapter                              | [0.005, 0.006]       |      16 |       1 |            | [40KB, 40KB]   | 1MB      |           |       0 | 0.01      
 11 |                            ->  Result                                   | [0.001, 0.002]       |      16 |       1 |            | [8KB, 8KB]     | 1MB      |           |       0 | 0.01      

可以看到,由于Nest Loop的存在,第8层物化算子需要用参考第7层表扫描的结果集,根据第9层的16个元组生成300791个元组之后,才能够进行Nest Loop。也就是说第8层的物化算子会被第7层阻塞,而第6层的Nest Loop算子又会被第8层的物化算子阻塞。同时,Nest Loop的上层算子是Aggregate,它属于物化算子,需要获取所有元组后才开始处理。

简而言之,该SQL的瓶颈在于Nest LoopAggregate都需要拿到下层算子给出的所有元组之后才能开始处理,且Nest Loop本身较慢,故尽管它们向上层算子都是流水线提交元组,在这里也造成了比较严重的阻塞。

既然找到了SQL慢的根因在于Nest Loop,那么解决的方法自然就是消除它。从SQL中可以看到,Nest Loop的作用在于把一个静态的列插入到子查询的结果集中。这个操作实际上可以通过把这个静态列直接写进子查询的SELECT中完成。

TMP作为一列放到T中后,性能有明显提升。改写后的SQL如下:

EXPLAIN PERFORMANCE
SELECT
  BG_CODE,
  BG_CN_NAME,
  BG_EN_NAME,
  METRIC_CODE --指标ID
,
  METRIC_CN_NAME --指标中文名称
,
  METRIC_EN_NAME --指标英文名称
,
  CURRENCY --币种
,
  OVERSEAS_FLAG,
  REGION_CODE,
  REGION_CN_NAME,
  REGION_EN_NAME,
  REPOFFICE_CODE,
  REPOFFICE_CN_NAME,
  REPOFFICE_EN_NAME,
  OFFICE_CODE,
  OFFICE_CN_NAME,
  OFFICE_EN_NAME,
  REGION_CUSTCATG_CODE,
  REGION_CUSTCATG_CN_NAME,
  REGION_CUSTCATG_EN_NAME,
  TOP_CUST_CATEGORY_CODE,
  TOP_CUST_CATEGORY_EN_NAME,
  TOP_CUST_CATEGORY_CN_NAME,
  ACCTCUST_HQ_CODE,
  ACCTCUST_HQ_CN_NAME,
  ACCTCUST_HQ_EN_NAME,
  ACCTCUST_BRANCH_CODE,
  ACCTCUST_BRANCH_CN_NAME,
  ACCTCUST_BRANCH_EN_NAME,
  ACCTCUST_SUBSIDIARY_CODE,
  ACCTCUST_SUBSIDIARY_CN_NAM,
  ACCTCUST_SUBSIDIARY_EN_NAM,
  COUNTRY_CODE --新增加入参
,
  COUNTRY_CN_NAME --新增加入参
,
  COUNTRY_EN_NAME --新增加入参
,
  AGREE_AMOUNT --BUSI_DSCT_00001   总优惠
,
  AGREE_REMAIN_AMOUNT --BUSI_DSCT_00002   即期优惠
,
  SIGN_AMOUNT --BUSI_DSCT_00003   即期优惠/一次性优惠
,
  USE_AMOUNT --BUSI_DSCT_00004   即期优惠/单价量折扣
,
  NOT_USED_VALID_AMOUNT --BUSI_DSCT_00005   延期优惠
,
  NOT_USED_INVALID_AMOUNT --BUSI_DSCT_00006   voucher
,
  NEW_SIGN_AMOUNT --BUSI_DSCT_00007   其他延期优惠
,
  NEW_USE_AMOUNT --BUSI_DSCT_00008   本月新使用金额
,
  EXPIRED_AMOUNT --BUSI_DSCT_00009   本月已过期金额
,
  IMMED_EXPIRED_AMOUNT --BUSI_DSCT_00010   半年内即将过期金额
FROM
  (
    SELECT
    case
      when length('[“202309“]') = 6 then '[“202309“]' || '01'
      WHEN length('[“202309“]') <> 8 THEN TO_CHAR(CURRENT_DATE, 'YYYYMMDD')
    END AS V_DATE,
      C.BG_CODE,
      C.BG_CN_NAME,
      C.BG_EN_NAME,
      C.M_ID AS METRIC_CODE --指标ID
,
      C.M_CN AS METRIC_CN_NAME --指标中文名称
,
      C.M_EN AS METRIC_EN_NAME --指标英文名称
,
      C.CURRENCY_CODE AS CURRENCY --币种
,CASE
        WHEN 1 = 0 THEN C.OVERSEA_FLAG
        ELSE NULL
      END AS OVERSEAS_FLAG,CASE
        WHEN 1 = 0 THEN C.REGION_CODE
        ELSE NULL
      END AS REGION_CODE,CASE
        WHEN 1 = 0 THEN C.REGION_CN_NAME
        ELSE NULL
      END AS REGION_CN_NAME,CASE
        WHEN 1 = 0 THEN C.REGION_EN_NAME
        ELSE NULL
      END AS REGION_EN_NAME,CASE
        WHEN 1 = 0 THEN C.REPOFFICE_CODE
        ELSE NULL
      END AS REPOFFICE_CODE,CASE
        WHEN 1 = 0 THEN C.REPOFFICE_CN_NAME
        ELSE NULL
      END AS REPOFFICE_CN_NAME,CASE
        WHEN 1 = 0 THEN C.REPOFFICE_EN_NAME
        ELSE NULL
      END AS REPOFFICE_EN_NAME,CASE
        WHEN 1 = 0 THEN C.OFFICE_CODE
        ELSE NULL
      END AS OFFICE_CODE,CASE
        WHEN 1 = 0 THEN C.OFFICE_CN_NAME
        ELSE NULL
      END AS OFFICE_CN_NAME,CASE
        WHEN 1 = 0 THEN C.OFFICE_EN_NAME
        ELSE NULL
      END AS OFFICE_EN_NAME,CASE
        WHEN 1 = 0 THEN C.REGION_CUSTCATG_CODE
        ELSE NULL
      END AS REGION_CUSTCATG_CODE,CASE
        WHEN 1 = 0 THEN C.REGION_CUSTCATG_CN_NAME
        ELSE NULL
      END AS REGION_CUSTCATG_CN_NAME,CASE
        WHEN 1 = 0 THEN C.REGION_CUSTCATG_EN_NAME
        ELSE NULL
      END AS REGION_CUSTCATG_EN_NAME,CASE
        WHEN 1 = 0 THEN C.TOP_CUST_CATEGORY_CODE
        ELSE NULL
      END AS TOP_CUST_CATEGORY_CODE,CASE
        WHEN 1 = 0 THEN C.TOP_CUST_CATEGORY_EN_NAME
        ELSE NULL
      END AS TOP_CUST_CATEGORY_EN_NAME,CASE
        WHEN 1 = 0 THEN C.TOP_CUST_CATEGORY_CN_NAME
        ELSE NULL
      END AS TOP_CUST_CATEGORY_CN_NAME,CASE
        WHEN 1 = 0 THEN C.ACCTCUST_HQ_CODE
        ELSE NULL
      END AS ACCTCUST_HQ_CODE,CASE
        WHEN 1 = 0 THEN C.ACCTCUST_HQ_CN_NAME
        ELSE NULL
      END AS ACCTCUST_HQ_CN_NAME,CASE
        WHEN 1 = 0 THEN C.ACCTCUST_HQ_EN_NAME
        ELSE NULL
      END AS ACCTCUST_HQ_EN_NAME,CASE
        WHEN 1 = 0 THEN C.ACCTCUST_BRANCH_CODE
        ELSE NULL
      END AS ACCTCUST_BRANCH_CODE,CASE
        WHEN 1 = 0 THEN C.ACCTCUST_BRANCH_CN_NAME
        ELSE NULL
      END AS ACCTCUST_BRANCH_CN_NAME,CASE
        WHEN 1 = 0 THEN C.ACCTCUST_BRANCH_EN_NAME
        ELSE NULL
      END AS ACCTCUST_BRANCH_EN_NAME,CASE
        WHEN 1 = 0 THEN C.ACCTCUST_SUBSIDIARY_CODE
        ELSE NULL
      END AS ACCTCUST_SUBSIDIARY_CODE,CASE
        WHEN 1 = 0 THEN C.ACCTCUST_SUBSIDIARY_CN_NAM
        ELSE NULL
      END AS ACCTCUST_SUBSIDIARY_CN_NAM,CASE
        WHEN 1 = 0 THEN C.ACCTCUST_SUBSIDIARY_EN_NAM
        ELSE NULL
      END AS ACCTCUST_SUBSIDIARY_EN_NAM,CASE
        WHEN 1 = 0 THEN C.COUNTRY_CODE
        ELSE NULL
      END AS COUNTRY_CODE --新增加入参
,CASE
        WHEN 1 = 0 THEN C.COUNTRY_CN_NAME
        ELSE NULL
      END AS COUNTRY_CN_NAME --新增加入参
,CASE
        WHEN 1 = 0 THEN C.COUNTRY_EN_NAME
        ELSE NULL
      END AS COUNTRY_EN_NAME --新增加入参
,
      SUM(C.AGREE_AMOUNT) AS AGREE_AMOUNT --协议金额
,
      SUM(C.AGREE_REMAIN_AMOUNT) AS AGREE_REMAIN_AMOUNT --协议剩余金额
,
      SUM(C.SIGN_AMOUNT) AS SIGN_AMOUNT --可用金额
,
      SUM(C.USE_AMOUNT) AS USE_AMOUNT --已使用金额
,
      SUM(
        CASE
          WHEN C.DSCT_TYPE = 'VOUCHER'
          AND NVL(
            C.EXPIRED_DATE,
            add_months(C.EFFECTIVE_DATE, C.VALID_MONTH)
          ) >= to_date(V_DATE, 'yyyymmdd') THEN C.AGREE_REMAIN_AMOUNT
          WHEN C.DSCT_TYPE in (
            'FOC',
            'Volume Based List Price Adjustment',
            'One-Time Discount'
          )
          AND C.DSCT_END_DATE >= to_date(V_DATE, 'yyyymmdd') THEN C.EFFECTIVE_TOTAL_AMOUNT
          ELSE NULL
        END
      ) AS NOT_USED_VALID_AMOUNT --未使用金额(有效期外)
,
      SUM(
        CASE
          WHEN C.DSCT_TYPE = 'VOUCHER' THEN C.SIGN_AMOUNT
          ELSE C.EFFECTIVE_TOTAL_AMOUNT
        END - CASE
          WHEN C.DSCT_TYPE = 'VOUCHER' THEN C.USE_AMOUNT
          ELSE C.USED_TOTAL_AMOUNT
        END - CASE
          WHEN C.DSCT_TYPE = 'VOUCHER'
          AND NVL(
            C.EXPIRED_DATE,
            add_months(C.EFFECTIVE_DATE, C.VALID_MONTH)
          ) >= to_date(V_DATE, 'yyyymmdd') THEN C.AGREE_REMAIN_AMOUNT
          WHEN C.DSCT_TYPE in (
            'FOC',
            'Volume Based List Price Adjustment',
            'One-Time Discount'
          )
          AND C.DSCT_END_DATE >= to_date(V_DATE, 'yyyymmdd') THEN C.EFFECTIVE_TOTAL_AMOUNT
          ELSE NULL
        END
      ) AS NOT_USED_INVALID_AMOUNT --未使用金额(有效期内)
,
      SUM(
        CASE
          WHEN C.DSCT_TYPE = 'VOUCHER'
          AND C.EXPIRED_DATE >= to_date(substr(V_DATE, 1, 6), 'yyyymm')
          and C.EXPIRED_DATE <= LAST_DAY(to_date(V_DATE, 'yyyymmdd')) THEN C.SIGN_AMOUNT
          WHEN C.DSCT_TYPE in (
            'FOC',
            'Volume Based List Price Adjustment',
            'One-Time Discount'
          )
          AND C.DSCT_START_DATE >= to_date(substr(V_DATE, 1, 6), 'yyyymm')
          and C.DSCT_START_DATE <= LAST_DAY(to_date(V_DATE, 'yyyymmdd')) THEN C.EFFECTIVE_TOTAL_AMOUNT
          ELSE NULL
        END
      ) AS NEW_SIGN_AMOUNT --本月新增可用金额
,
      SUM(
        CASE
          WHEN C.DSCT_TYPE = 'VOUCHER'
          AND C.EFFECTIVE_DATE >= to_date(substr(V_DATE, 1, 6), 'yyyymm')
          and C.EFFECTIVE_DATE <= LAST_DAY(to_date(V_DATE, 'yyyymmdd')) THEN C.USE_AMOUNT
          WHEN C.DSCT_TYPE in (
            'FOC',
            'Volume Based List Price Adjustment',
            'One-Time Discount'
          )
          AND C.DSCT_START_DATE >= to_date(substr(V_DATE, 1, 6), 'yyyymm')
          and C.DSCT_START_DATE <= LAST_DAY(to_date(V_DATE, 'yyyymmdd')) THEN C.USED_TOTAL_AMOUNT
          ELSE NULL
        END
      ) AS NEW_USE_AMOUNT --本月新使用金额
,
      SUM(
        CASE
          WHEN C.DSCT_TYPE = 'VOUCHER'
          AND C.EXPIRED_DATE < to_date(V_DATE, 'yyyymmdd') THEN C.AGREE_REMAIN_AMOUNT
          WHEN C.DSCT_TYPE in (
            'FOC',
            'Volume Based List Price Adjustment',
            'One-Time Discount'
          )
          AND C.DSCT_END_DATE < to_date(V_DATE, 'yyyymmdd') THEN C.EFFECTIVE_TOTAL_AMOUNT
          ELSE NULL
        END
      ) AS EXPIRED_AMOUNT --本月已过期金额
,
      SUM(
        CASE
          WHEN C.DSCT_TYPE = 'VOUCHER'
          AND C.EXPIRED_DATE BETWEEN to_date(V_DATE, 'yyyymmdd')
          AND add_months(to_date(V_DATE, 'yyyymmdd'), 6) THEN C.AGREE_REMAIN_AMOUNT
          WHEN C.DSCT_TYPE in (
            'FOC',
            'Volume Based List Price Adjustment',
            'One-Time Discount'
          )
          AND C.DSCT_END_DATE BETWEEN to_date(V_DATE, 'yyyymmdd')
          AND add_months(to_date(V_DATE, 'yyyymmdd'), 6) THEN C.EFFECTIVE_TOTAL_AMOUNT
          ELSE NULL
        END
      ) AS IMMED_EXPIRED_AMOUNT --半年内即将过期金额
    FROM
      DMSALESW.DM_SALE_BUSI_DSCT_SUM_F C
    WHERE
      C.CURRENCY_CODE IN ('USD') --改为多值
      AND C.BG_CODE IN ('PDCG901159')
      AND C.M_ID IN (
        'BUSI_DSCT_00001',
        'BUSI_DSCT_00002',
        'BUSI_DSCT_00003',
        'BUSI_DSCT_00004',
        'BUSI_DSCT_00005',
        'BUSI_DSCT_00006',
        'BUSI_DSCT_00007'
      ) --新增加字段
      --AND C.M_CN IN ('#[#P_REPORT_ITEM_NAME#]#') --新增加字段
      --新增加字段
    GROUP BY
      1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36
  ) T --新增加入参

下面是改写后SQL的执行计划:

 id |                                operation                                |        A-time        | A-rows  | E-rows  | E-distinct |  Peak Memory   | E-memory |  A-width  | E-width |  E-costs  
----+-------------------------------------------------------------------------+----------------------+---------+---------+------------+----------------+----------+-----------+---------+-----------
  1 | ->  Row Adapter                                                         | 1139.637             |       7 |     245 |            | 419KB          |          |           |    1318 | 117002.27 
  2 |    ->  Vector Streaming (type: GATHER)                                  | 1139.616             |       7 |     245 |            | 777KB          |          |           |    1318 | 117002.27 
  3 |       ->  Vector Subquery Scan on t                                     | [1129.463, 1130.072] |       7 |     245 |            | [504KB, 504KB] | 1MB      |           |    1318 | 116920.22 
  4 |          ->  Vector Hash Aggregate                                      | [1129.459, 1130.067] |       7 |     245 |            | [4MB, 4MB]     | 16MB     | [0,898]   |     523 | 116920.07 
  5 |             ->  Vector Streaming(type: REDISTRIBUTE)                    | [1129.142, 1129.918] |     112 |    3920 |            | [1MB, 1MB]     | 2MB      |           |     523 | 116643.28 
  6 |                ->  Vector Hash Aggregate                                | [882.194, 987.474]   |     112 |    3920 |            | [8MB, 8MB]     | 16MB     | [861,861] |     523 | 116498.95 
  7 |                   ->  CStore Scan on dmsalesw.dm_sale_busi_dsct_sum_f c | [126.343, 142.697]   | 3080954 | 2135243 |            | [5MB, 5MB]     | 1MB      |           |     203 | 66116.77  

可以看到,不但省去了Nest Loop的耗时,而且后面Aggregate的耗时也减少了不少。整体从3s+优化到1.2s。

    附件下载

  • 6_plan.txt 84.66KB 下载次数:0
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。