2012-09-03 36 views
0

我有一大組數據,用於根據當前日期按功能分組顯示在指定日期範圍內的兩個表中的一系列產品的數據。數據範圍是:如何通過指定年和月的範圍進行分組?

可以說,今天的日期爲03/09/2012(DD/MM/YYYY)

--Product 1-- 
Everything 5 years ago 'Before 2007' {field1} {field2} {field3} 
4 years ago   '2008' 
3 years ago   '2009' 
2 years ago   '2010' 
1 year by month  'Jan 2011' 
'Feb 2011' 
'Mar 2011 
.... 
.... 
'Dec 2011' 
Sum of 1 year ago '2011' 
This year by month 'Jan 2012' 
'Feb 2012' 
'Mar 2012' 
.... 
.... 
'Sept 2012' 
Sum of this year '2012' 

這個SQL的性能是非常重要的。到目前爲止,我得到了一個sql,可以按年份或月份對每個產品進行進一步分組,但不按以上順序進行分組。我想使用NVL,CASE和許多嵌套的SQL,但任何人都可以想到一個可以獲得良好性能的解決方案?

SELECT EXTRACT (YEAR FROM {DATE}) "YEAR", EXTRACT (MONTH FROM {DATE}) "MONTH", SUM({field1}) as A, SUM({field 2}) as B ,COUNT(1) as {field 3} 
    FROM (
      SELECT {Field A}, DECODE({Field Key1}, NULL, 0, 1) {field 1}, DECODE({field B}, NULL, 1, 0) {field2}, {Field Key2} 
      FROM {table A}, (
        SELECT {field key2} 
        FROM {table B} 
        WHERE {conditions} B 
      WHERE A.KEY= B.KEY(+) 
    ) 
    where {conditions} 
    GROUP BY EXTRACT (YEAR FROM {DATE}) , EXTRACT (MONTH FROM {DATE}) 
) DATASET 

回答

1

我有點不清楚你在掙扎哪一部分,或者爲什麼你是指許多嵌套的SQL。在沒有任何樣本數據,我不得不做出一些了:

create table t42 (my_date date, my_value number); 

insert into t42 values (date '2006-01-31', 0601); 
insert into t42 values (date '2006-12-31', 0612); 
insert into t42 values (date '2007-01-31', 0701); 
insert into t42 values (date '2007-12-31', 0712); 
insert into t42 values (date '2008-01-31', 0801); 
insert into t42 values (date '2008-12-31', 0812); 
insert into t42 values (date '2009-01-31', 0901); 
insert into t42 values (date '2009-12-31', 0912); 
insert into t42 values (date '2010-01-31', 1001); 
insert into t42 values (date '2010-12-31', 1012); 
insert into t42 values (date '2011-01-31', 1101); 
insert into t42 values (date '2011-12-31', 1112); 
insert into t42 values (date '2012-01-31', 1201); 
insert into t42 values (date '2012-02-29', 1202); 
insert into t42 values (date '2012-03-31', 1203); 
insert into t42 values (date '2012-04-30', 1204); 
insert into t42 values (date '2012-05-31', 1205); 

然後,您可以使用一個內部查詢產生期間/年/月的「標籤」,並插入一個空場被責令結果,再加上你的價值觀真正感興趣。然後,一個外部查詢做任何sumcount

select label, sum(my_value), count(1) 
from 
(
    select 
     case when my_date < trunc(sysdate, 'YYYY') - interval '4' year then 
       'Before ' || to_char(trunc(sysdate, 'YYYY') 
       - interval '5' year, 'YYYY') 
      when my_date < trunc(sysdate, 'YYYY') - interval '1' year then 
       to_char(my_date, 'YYYY') 
      else to_char(my_date, 'Mon YYYY') 
     end as label, 
     case when my_date < trunc(sysdate, 'YYYY') - interval '4' year then 
       to_char(trunc(sysdate, 'YYYY') - interval '5' year, 'YYYYMM') 
      when my_date < trunc(sysdate, 'YYYY') - interval '1' year then 
       to_char(my_date, 'YYYY') || '01' 
      else to_char(my_date, 'YYYYMM') 
     end as order_field, 
     my_value 
    from t42 
) 
group by label, order_field 
order by order_field; 

我使用trunc(sysdate, 'YYYY')找到當年開始,然後使用interval去回溯五年,並且基於那些生產labelorder_field假專欄「水桶」。這樣使用case可以讓我擁有不同的水桶 - 一個超過五年的水桶,一年之前的一年,一年之後的水桶。

LABEL    SUM(MY_VALUE) COUNT(1) 
----------------- ------------- ---------- 
Before 2007    2626   4 
2008      1613   2 
2009      1813   2 
2010      2013   2 
Jan 2011     1101   1 
Dec 2011     1112   1 
Jan 2012     1201   1 
Feb 2012     1202   1 
Mar 2012     1203   1 
Apr 2012     1204   1 
May 2012     1205   1 

我只打表一次,這樣的表現應該取決於你如何提取原始數據(你的連接和條件),而不是你如何操縱它。很明顯,你可以用你當前替換t42你的兩個表之間的連接,並找出你感興趣的領域。

我建議你切換到ANSI連接語法,而不是Oracle的老(+)記法外連接。這不涉及任何沒有任何數據的年份或月份,但您的原始大綱也沒有,因此這可能不是問題。而且,如何生成或至少顯示「年份總和」值可能取決於您的客戶。


分離出標籤生成到一個視圖,以使其可重複使用,讓你發現沒有數據的時間段:

create or replace view v42 (period_label, period_order, period_start, period_end) 
as 
select 'Before ' || to_char(trunc(sysdate, 'YYYY') - interval '5' year, 'YYYY'), 
    '197001', 
    date '1970-01-01', 
    trunc(sysdate, 'YYYY') - interval '4' year - interval '1' second 
from dual 
union 
select to_char(year_start, 'YYYY'), 
    to_char(year_start, 'YYYY') || '01', 
    year_start, 
    year_start + interval '1' year - interval '1' second 
from (
    select add_months(trunc(sysdate, 'YYYY'), - 12 * (level + 1)) as year_start 
    from dual connect by level <= 3 
) 
union 
select to_char(month_start, 'Mon YYYY'), 
    to_char(month_start, 'YYYYMM'), 
    month_start, 
    month_start + interval '1' month - interval '1' second 
from (
    select add_months(trunc(sysdate, 'MM'), 1 - level) as month_start 
    from dual connect by level <= 12 + to_number(to_char(sysdate, 'MM')) 
); 

這是產生你原本的標籤;如果您希望顯示所有年份單獨刪除union的第一部分,並且您可以調整connect by子句以更改按月顯示的年份。 (你可能會參數化,但這可能會有點遠)。

你有三類「桶」,一個按月分類,一個按年分類,然後是過去的任何東西都可以捕獲所有的東西;聯盟的每個部分都會處理其中一個部分,爲每個類中的所有桶生成期間開始和結束日期,以及稍後訂購的標籤和某些內容。查看視圖,也可以分別查看每個select,看看他們在做什麼。

然後將該視圖加入到您的數據表或表中;與左外連接,如果你想不匹配的數據顯示標籤:

select v.period_label, nvl(sum(t.my_value), 0), count(t.my_value) 
from v42 v 
left join t42 t on t.my_date between v.period_start and v.period_end 
group by v.period_label, v.period_order 
order by v.period_order; 

PERIOD_LABEL  NVL(SUM(T.MY_VALUE),0) COUNT(T.MY_VALUE) 
----------------- ---------------------- ----------------- 
Before 2007       2626     4 
2008        1613     2 
2009        1813     2 
2010        2013     2 
Jan 2011       1101     1 
Feb 2011        0     0 
... 
Nov 2011        0     0 
Dec 2011       1112     1 
... 
+0

對不起,這是我第一次post.My甲骨文技能生疏,只能再次把它撿起來幾個月前。感謝您的詳細解釋和建議。您的解決方案解決了我的問題的90%。有幾個細節我仍然在苦苦掙扎。 1:有20多種產品,每種產品都需要一張結果表,外觀完全相同。 2:並非所有產品都具有「my_date」值來繪製標籤。 3:最近3年需要總數,但可以在水晶報表上計算。這基本上是一個產品銷售的OLAP立方體分析,具有指定期限,必須在SP報告中寫入Crystal報告。 – thzNewbie

+0

@thzNewbie - 這真的不是一個批評,更多的是我不確定我是否真的在解決你的問題 - 看起來我很高興,主要是無論如何。如果'桶'是你需要的,那麼我猜你的主要問題是將底層數據轉化爲正確的形式和位置 - 用'from'替換'from t42'(select ... from table_a join table_b ...在哪裏......)等等。你當然可以有更多的列,你只需要把它們放在你的內部select中,然後將它們聚合或者將它們添加到'group by'中。 –

+0

任何解決標籤問題的解決方案,以便它始終創建一個固定的視圖?除了創建一個表與我想要的指定日期範圍的組合。即生成這個年份和月份組合的列表 – thzNewbie

0
select * from (
    select t.* 
     ,case 
      when extract(YEAR FROM t.date) < 2007 
       then 0 
      else 
       extract(YEAR FROM t.date) 
      end as nYear 
     ,case 
      when extract(YEAR FROM t.date) < 2007 
       then 0 
      else 
       extract(MONTH FROM t.date) 
      end as nMonth  
    from table_name t 
)d 
group by d.nYear, d.nMonth 
相關問題