2015-10-09 65 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