【问题标题】:Tuning oracle subquery in select statement在 select 语句中调整 oracle 子查询
【发布时间】:2015-12-19 08:19:53
【问题描述】:

我有一个主表和一个参考表,如下所示。

WITH MAS as (
SELECT 10 as CUSTOMER_ID, 1 PROCESS_ID, 44 PROCESS_TYPE, 200 as AMOUNT FROM DUAL UNION ALL
SELECT 10 as CUSTOMER_ID, 1 PROCESS_ID, 44 PROCESS_TYPE, 250 as AMOUNT FROM DUAL UNION ALL
SELECT 10 as CUSTOMER_ID, 2 PROCESS_ID, 45 PROCESS_TYPE, 300 as AMOUNT FROM DUAL UNION ALL
SELECT 10 as CUSTOMER_ID, 2 PROCESS_ID, 45 PROCESS_TYPE, 350 as AMOUNT FROM DUAL 
), REFTAB as (
SELECT 44 PROCESS_TYPE, 'A' GROUP_ID FROM DUAL UNION ALL 
SELECT 44 PROCESS_TYPE, 'B' GROUP_ID FROM DUAL UNION ALL
SELECT 45 PROCESS_TYPE, 'C' GROUP_ID FROM DUAL UNION ALL 
SELECT 45 PROCESS_TYPE, 'D' GROUP_ID FROM DUAL
) SELECT ...

我的第一个正确工作的select 语句是这个:

SELECT CUSTOMER_ID,
       SUM(AMOUNT) as AMOUNT1,
       SUM(CASE WHEN PROCESS_TYPE IN (SELECT PROCESS_TYPE FROM REFTAB WHERE GROUP_ID = 'A') 
                THEN AMOUNT ELSE NULL END) as AMOUNT2,
       COUNT(CASE WHEN PROCESS_TYPE IN (SELECT PROCESS_TYPE FROM REFTAB WHERE GROUP_ID = 'D') 
                  THEN 1 ELSE NULL END) as COUNT1
   FROM MAS
  GROUP BY CUSTOMER_ID

但是,为了解决性能问题,我将其更改为 select 声明:

SELECT CUSTOMER_ID,
       SUM(AMOUNT) as AMOUNT1,
       SUM(CASE WHEN GROUP_ID = 'A' THEN AMOUNT ELSE NULL END) as AMOUNT2,
       COUNT(CASE WHEN GROUP_ID = 'D' THEN 1 ELSE NULL END) as COUNT1
   FROM MAS A
   LEFT JOIN REFTAB B ON A.PROCESS_TYPE = B.PROCESS_TYPE
  GROUP BY CUSTOMER_ID

对于AMOUNT2COUNT1 列,值保持不变。但是对于AMOUNT1,由于与引用表的连接,值会相乘。

我知道我可以在 GROUP_ID 上再添加 1 个左连接和一个附加连接条件。但这与使用子查询没有什么不同。

知道如何在不乘以 AMOUNT1 值的情况下使查询仅使用 1 个左连接工作吗?

【问题讨论】:

    标签: sql oracle11g sqlperformance query-tuning


    【解决方案1】:

    我知道我可以通过添加额外的 GROUP_ID 子句再添加 1 个左连接,但它不会与子查询不同。

    你会感到惊讶。在 SELECT 中有 2 个左连接而不是子查询为优化器提供了更多优化查询的方法。我仍然会尝试:

    select m.customer_id,
           sum(m.amount) as amount1,
           sum(case when grpA.group_id is not null then m.amount end) as amount2,
           count(grpD.group_id) as count1
      from mas m
      left join reftab grpA
        on grpA.process_type = m.process_type
       and grpA.group_id = 'A'
      left join reftab grpD
        on grpD.process_type = m.process_type
       and grpD.group_id = 'D'
     group by m.customer_id
    

    你也可以试试这个查询,它使用SUM()解析函数计算amount1的值连接之前避免重复值问题:

    select m.customer_id,
           m.customer_sum as amount1,
           sum(case when r.group_id = 'A' then m.amount end) as amount2,
           count(case when r.group_id = 'D' then 'X' end) as count1
      from (select customer_id,
                   process_type,
                   amount,
                   sum(amount) over (partition by customer_id) as customer_sum
              from mas) m
      left join reftab r
        on r.process_type = m.process_type
     group by m.customer_id,
              m.customer_sum
    

    您可以测试这两种选择,看看哪一种表现更好。

    【讨论】:

      【解决方案2】:

      从您的原始查询开始,只需将您的 IN 查询替换为 EXISTS 语句即可显着提升。另外,请注意汇总NULLs,也许您的ELSE 语句应该是0

      SELECT CUSTOMER_ID,
             SUM(AMOUNT) as AMOUNT1,
             SUM(CASE WHEN EXISTS(SELECT 1 FROM REFTAB WHERE REFTAB.GROUP_ID = 'A' AND REFTAB.PROCESS_TYPE = MAS.PROCESS_TYPE)
                      THEN AMOUNT ELSE NULL END) as AMOUNT2,
             COUNT(CASE WHEN EXISTS(SELECT 1 FROM REFTAB WHERE REFTAB.GROUP_ID = 'D' AND REFTAB.PROCESS_TYPE = MAS.PROCESS_TYPE) 
                        THEN 1 ELSE NULL END) as COUNT1
         FROM MAS
        GROUP BY CUSTOMER_ID
      

      【讨论】:

        【解决方案3】:

        正常的方法是聚合group by之前的值。如果查询的其余部分正确,您还可以使用条件聚合:

        SELECT CUSTOMER_ID,
               SUM(CASE WHEN seqnum = 1 THEN AMOUNT END) as AMOUNT1,
               SUM(CASE WHEN GROUP_ID = 'A' THEN AMOUNT ELSE NULL END) as AMOUNT2,
               COUNT(CASE WHEN GROUP_ID = 'D' THEN 1 ELSE NULL END) as COUNT1
        FROM MAS A LEFT JOIN
             (SELECT B.*, ROW_NUMBER() OVER (PARTITION BY PROCESS_TYPE ORDER BY PROCESS_TYPE) as seqnum
              FROM REFTAB B
             ) B
             ON A.PROCESS_TYPE = B.PROCESS_TYPE
        GROUP BY CUSTOMER_ID;
        

        这会忽略连接创建的重复项。

        【讨论】:

          猜你喜欢
          • 2011-07-12
          • 2023-02-25
          • 1970-01-01
          • 2014-09-01
          • 2016-08-17
          • 1970-01-01
          • 2023-04-04
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多