2015-10-09 43 views
2

我有一个相当大的查询被用来获得一堆结果,而且我几乎可以肯定这不是做到这一点的方法。它很肮脏。这是可恶的。首先,让我解释一下我所希望的表结构:Oracle SQL:简化我看似可怕的子查询集合

+------------------+------+------+------+------+--------+ 
| CURRENT_DATE | RO | FL | LM | AO | TOTAL | 
+------------------+------+------+------+------+--------+ 
| 1/2/2012  | 31 | 33 | 70 | 10 | 144 | 
+------------------+------+------+------+------+--------+ 

以上数据集从下表收集:

+---------------+--------------------+ 
| CURRENT_DATE | PORTABLE_PEANUTS | 
+---------------+--------------------+ 
| 1/2/2012 |   RO   | 
+---------------+--------------------+ 
| 2/4/2013 |   FL   | 
+---------------+--------------------+ 
| 3/6/2014 |   LM   | 
+---------------+--------------------+ 
| 4/8/2015 |   AO   | 
+---------------+--------------------+ 

从本质上讲,我试图收集是发生在PORTABLE_PEANUTS上有一定的一切日期,发生的频率以及发生了什么。

下面是我使用的查询:

SELECT total.CURRENT_DATE, results.RO, results.FL, results.LM, results.AO, total.TOTAL FROM 
(
    SELECT CURRENT_DATE, SUM(RO+FL+LM+AO) TOTAL FROM 
    (
     SELECT a.CURRENT_DATE, a.RO, b.FL, c.LM, d.AO FROM 
     (
      SELECT TO_CHAR(SESSION_DATE, 'yyyy-mm-dd') CURRENT_DATE, COUNT(PORTABLE_PEANUTS) RO FROM CORE.DATE_TEST 
      WHERE PORTABLE_PEANUTS LIKE 'RO' 
     GROUP BY TO_CHAR(SESSION_DATE, 'yyyy-mm-dd') 
    ) a 
    JOIN 
    (
     SELECT TO_CHAR(SESSION_DATE, 'yyyy-mm-dd') CURRENT_DATE, COUNT(PORTABLE_PEANUTS) FL FROM CORE.DATE_TEST 
      WHERE PORTABLE_PEANUTS LIKE 'FL' 
     GROUP BY TO_CHAR(SESSION_DATE, 'yyyy-mm-dd')  
    ) b ON a.CURRENT_DATE = b.CURRENT_DATE 
    JOIN 
    (
     SELECT TO_CHAR(SESSION_DATE, 'yyyy-mm-dd') CURRENT_DATE, COUNT(PORTABLE_PEANUTS) LM FROM CORE.DATE_TEST 
      WHERE PORTABLE_PEANUTS LIKE 'LM' 
     GROUP BY TO_CHAR(SESSION_DATE, 'yyyy-mm-dd')  
    ) c ON a.CURRENT_DATE = c.CURRENT_DATE 
    JOIN 
    (
     SELECT TO_CHAR(SESSION_DATE, 'yyyy-mm-dd') CURRENT_DATE, COUNT(PORTABLE_PEANUTS) AO FROM CORE.DATE_TEST 
      WHERE PORTABLE_PEANUTS LIKE 'AO' 
     GROUP BY TO_CHAR(SESSION_DATE, 'yyyy-mm-dd')  
    ) d ON a.CURRENT_DATE = d.CURRENT_DATE 
) 
GROUP BY CURRENT_DATE 
) total 
JOIN 
(
    SELECT a.CURRENT_DATE, a.RO, b.FL, c.LM, d.AO FROM 
    (
     SELECT TO_CHAR(SESSION_DATE, 'yyyy-mm-dd') CURRENT_DATE, COUNT(PORTABLE_PEANUTS) RO FROM CORE.DATE_TEST 
      WHERE PORTABLE_PEANUTS LIKE 'RO' 
     GROUP BY TO_CHAR(SESSION_DATE, 'yyyy-mm-dd') 
    ) a 
    JOIN 
    (
     SELECT TO_CHAR(SESSION_DATE, 'yyyy-mm-dd') CURRENT_DATE, COUNT(PORTABLE_PEANUTS) FL FROM CORE.DATE_TEST 
      WHERE PORTABLE_PEANUTS LIKE 'FL' 
     GROUP BY TO_CHAR(SESSION_DATE, 'yyyy-mm-dd')  
    ) b ON a.CURRENT_DATE = b.CURRENT_DATE 
    JOIN 
    (
     SELECT TO_CHAR(SESSION_DATE, 'yyyy-mm-dd') CURRENT_DATE, COUNT(PORTABLE_PEANUTS) LM FROM CORE.DATE_TEST 
      WHERE PORTABLE_PEANUTS LIKE 'LM' 
     GROUP BY TO_CHAR(SESSION_DATE, 'yyyy-mm-dd')  
    ) c ON a.CURRENT_DATE = c.CURRENT_DATE 
    JOIN 
    (
     SELECT TO_CHAR(SESSION_DATE, 'yyyy-mm-dd') CURRENT_DATE, COUNT(PORTABLE_PEANUTS) AO FROM CORE.DATE_TEST 
      WHERE PORTABLE_PEANUTS LIKE 'AO' 
     GROUP BY TO_CHAR(SESSION_DATE, 'yyyy-mm-dd')  
    ) d ON a.CURRENT_DATE = d.CURRENT_DATE 
) results ON total.CURRENT_DATE = results.CURRENT_DATE 
ORDER BY CURRENT_DATE ASC; 

现在这个查询工作,并且相对来说,它的速度不够快,但它看起来丑陋。这看起来很难维护,我很确定我在这里错过了一些东西。

回答

3

它看起来像你想有条件聚集:

SELECT TO_CHAR(SESSION_DATE, 'yyyy-mm-dd') as CURRENT_DATE, 
     SUM(CASE WHEN PORTABLE_PEANUTS = 'RO' THEN 1 ELSE 0 END) as RO, 
     SUM(CASE WHEN PORTABLE_PEANUTS = 'FL' THEN 1 ELSE 0 END) as FL, 
     SUM(CASE WHEN PORTABLE_PEANUTS = 'LM' THEN 1 ELSE 0 END) as LM, 
     SUM(CASE WHEN PORTABLE_PEANUTS = 'AO' THEN 1 ELSE 0 END) as AO, 
     SUM(CASE WHEN PORTABLE_PEANUTS IN ('RO', 'FL', 'LM', 'AO') THEN 1 ELSE 0 END) as TOTAL   
FROM CORE.DATE_TEST 
GROUP BY TO_CHAR(SESSION_DATE, 'yyyy-mm-dd') 

我不知道你想做什么。这可能实际上处理整个查询。

+0

哇,那真的很棒。有条件的聚合 - 我必须记住这一点。它不仅能够以更少的步骤完成我想要的操作,而且它的速度提高了一百倍。 –

1

我想你可以简单地使用一个支点查询 - 假设你在Oracle 11g或更高,如:

with date_test as (select to_date('01/02/2012', 'dd/mm/yyyy') session_date, 'RO' portable_peanuts from dual union all 
        select to_date('01/02/2012', 'dd/mm/yyyy') session_date, 'RO' portable_peanuts from dual union all 
        select to_date('02/02/2012', 'dd/mm/yyyy') session_date, 'RO' portable_peanuts from dual union all 
        select to_date('02/02/2012', 'dd/mm/yyyy') session_date, 'RO' portable_peanuts from dual union all 
        select to_date('01/02/2012', 'dd/mm/yyyy') session_date, 'FL' portable_peanuts from dual union all 
        select to_date('01/02/2012', 'dd/mm/yyyy') session_date, 'FL' portable_peanuts from dual union all 
        select to_date('02/02/2012', 'dd/mm/yyyy') session_date, 'FL' portable_peanuts from dual union all 
        select to_date('02/02/2012', 'dd/mm/yyyy') session_date, 'LM' portable_peanuts from dual union all 
        select to_date('02/02/2012', 'dd/mm/yyyy') session_date, 'LM' portable_peanuts from dual union all 
        select to_date('02/02/2012', 'dd/mm/yyyy') session_date, 'LM' portable_peanuts from dual union all 
        select to_date('01/02/2012', 'dd/mm/yyyy') session_date, 'AO' portable_peanuts from dual union all 
        select to_date('01/02/2012', 'dd/mm/yyyy') session_date, 'AO' portable_peanuts from dual union all 
        select to_date('01/02/2012', 'dd/mm/yyyy') session_date, 'AO' portable_peanuts from dual union all 
        select to_date('02/02/2012', 'dd/mm/yyyy') session_date, 'AO' portable_peanuts from dual union all 
        select to_date('02/02/2012', 'dd/mm/yyyy') session_date, 'AO' portable_peanuts from dual) 
-- end of mimicking sample data 
select to_char(session_date, 'yyyy-mm-dd') current_date, 
     ro, 
     fl, 
     lm, 
     ao, 
     ro + fl + lm + ao total 
from date_test 
pivot (count(portable_peanuts) 
     for portable_peanuts in ('RO' as ro, 'FL' as fl, 'LM' as lm, 'AO' as ao)) 
order by to_char(session_date, 'yyyy-mm-dd'); 

CURRENT_DATE   RO   FL   LM   AO  TOTAL 
------------ ---------- ---------- ---------- ---------- ---------- 
2012-02-01   2   2   0   3   7 
2012-02-02   2   1   3   2   8 

另外,如果你在10g或之前,那么你就可以用做枢轴的旧式方式,通过使用情况报表和汇总:

with date_test as (select to_date('01/02/2012', 'dd/mm/yyyy') session_date, 'RO' portable_peanuts from dual union all 
        select to_date('01/02/2012', 'dd/mm/yyyy') session_date, 'RO' portable_peanuts from dual union all 
        select to_date('02/02/2012', 'dd/mm/yyyy') session_date, 'RO' portable_peanuts from dual union all 
        select to_date('02/02/2012', 'dd/mm/yyyy') session_date, 'RO' portable_peanuts from dual union all 
        select to_date('01/02/2012', 'dd/mm/yyyy') session_date, 'FL' portable_peanuts from dual union all 
        select to_date('01/02/2012', 'dd/mm/yyyy') session_date, 'FL' portable_peanuts from dual union all 
        select to_date('02/02/2012', 'dd/mm/yyyy') session_date, 'FL' portable_peanuts from dual union all 
        select to_date('02/02/2012', 'dd/mm/yyyy') session_date, 'LM' portable_peanuts from dual union all 
        select to_date('02/02/2012', 'dd/mm/yyyy') session_date, 'LM' portable_peanuts from dual union all 
        select to_date('02/02/2012', 'dd/mm/yyyy') session_date, 'LM' portable_peanuts from dual union all 
        select to_date('01/02/2012', 'dd/mm/yyyy') session_date, 'AO' portable_peanuts from dual union all 
        select to_date('01/02/2012', 'dd/mm/yyyy') session_date, 'AO' portable_peanuts from dual union all 
        select to_date('01/02/2012', 'dd/mm/yyyy') session_date, 'AO' portable_peanuts from dual union all 
        select to_date('02/02/2012', 'dd/mm/yyyy') session_date, 'AO' portable_peanuts from dual union all 
        select to_date('02/02/2012', 'dd/mm/yyyy') session_date, 'AO' portable_peanuts from dual) 
-- end of mimicking sample data 
select to_char(session_date, 'yyyy-mm-dd') current_date, 
     count(case when portable_peanuts = 'RO' then 1 end) ro, 
     count(case when portable_peanuts = 'FL' then 1 end) fl, 
     count(case when portable_peanuts = 'LM' then 1 end) lm, 
     count(case when portable_peanuts = 'AO' then 1 end) ao, 
     count(*) total 
from  date_test 
where portable_peanuts in ('RO', 'FL', 'LM', 'AO') 
group by to_char(session_date, 'yyyy-mm-dd') 
order by to_char(session_date, 'yyyy-mm-dd'); 

CURRENT_DATE   RO   FL   LM   AO  TOTAL 
------------ ---------- ---------- ---------- ---------- ---------- 
2012-02-01   2   2   0   3   7 
2012-02-02   2   1   3   2   8 

而且,按照要求,这里有以上两个查询,而不采样数据:

select to_char(session_date, 'yyyy-mm-dd') current_date, 
     ro, 
     fl, 
     lm, 
     ao, 
     ro + fl + lm + ao total 
from date_test 
pivot (count(portable_peanuts) 
     for portable_peanuts in ('RO' as ro, 'FL' as fl, 'LM' as lm, 'AO' as ao)) 
order by to_char(session_date, 'yyyy-mm-dd'); 


select to_char(session_date, 'yyyy-mm-dd') current_date, 
     count(case when portable_peanuts = 'RO' then 1 end) ro, 
     count(case when portable_peanuts = 'FL' then 1 end) fl, 
     count(case when portable_peanuts = 'LM' then 1 end) lm, 
     count(case when portable_peanuts = 'AO' then 1 end) ao, 
     count(*) total 
from  date_test 
where portable_peanuts in ('RO', 'FL', 'LM', 'AO') 
group by to_char(session_date, 'yyyy-mm-dd') 
order by to_char(session_date, 'yyyy-mm-dd'); 

N.B.从另一个问题中,我发现你将在两个日期之间查询行。

在这种情况下,不是将DATE列转换为字符串,而是将其保留为日期,但将其截断为使其在日期级别进行分组。

即,代替使用to_char(session_date, 'yyyy-mm-dd'),只使用trunc(session_date)

这样一来,你的where子句将被两种:

where session_date >= to_date(p_start_date, p_date_format) 
and session_date < to_date (p_end_date, p_date_format) 

where trunc(session_date) >= to_date(p_start_date, p_date_format) 
and trunc(session_date) < to_date (p_end_date, p_date_format) 

取决于您希望如何处理session_date的时间部分。前者将能够在session_date列上使用索引,而如果您希望使用索引,则需要在trunc(session_date)上创建基于函数的索引。

+0

不错的尝试,我很高兴你,因为它仍然是一个简短的版本,但请查看@Gordon Linoff的答案。这正是我所期待的。 –

+1

“不错的尝试”?我给了你两种方法,其中一种与Gordon的方法几乎相同(它使用count而不是sum),其中一种方法是Oracle提供的方法。我想没有人会喜欢! * {;-) – Boneist

+1

什么,仅仅因为我添加了一些示例数据,以便您可以将它作为自包含的测试用例运行?你确实知道你可以跳过顶部“用date_test作为(...)”子句,对吧? – Boneist