繁体   English   中英

在select语句中调整oracle子查询

[英]Tuning oracle subquery in select statement

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

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 ,由于与参考表的AMOUNT1 ,该值被相乘。

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

任何想法如何使查询仅与1个左AMOUNT1一起工作,而又不乘AMOUNT1值?

通常的方法是在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;

这将忽略由联接创建的重复项。

我知道我可以再添加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

您可以测试两个选项,然后查看哪个选项效果更好。

从原始查询开始,简单地用EXISTS语句替换IN查询应该会大大提高。 另外,请注意将NULL相加,也许您的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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM