2012-06-13 22 views
2

我想象的結果會是什麼樣子:如何按類別按年份分組,以便每個類別包含每年的零金額?

Category | Year | sum | 
--------- ------ -------- 
    A  2008 200 
    A  2009  0 
    B  2008 100 
    B  2009  5 
    ...  ...  ... 

即每類別交易的總和。

存在其中類別沒有任何交易爲一年的情況。在這些情況下,結果的第二行不會出現。我如何重新編寫上述查詢以便將2008,2009包含在每個類別中?

select category, to_char(trans_date, 'YYYY') year, sum(trans_value) 
from transaction 
group by category, to_char(trans_date, 'YYYY') 
order by 1, 2; 

回答

1

隨着partitioned outer join,你並不需要一個類別表。

我用同樣的事務表中使用的「DCP」:

SQL> create table transactions 
    2 (category varchar(1) 
    3 , trans_date date 
    4 , trans_value number(25,8) 
    5 ); 

Table created. 

SQL> insert into transactions values ('A',to_date('2008-01-01','yyyy-mm-dd'),100.0); 

1 row created. 

SQL> insert into transactions values ('A',to_date('2008-02-01','yyyy-mm-dd'),100.0); 

1 row created. 

SQL> insert into transactions values ('B',to_date('2008-01-01','yyyy-mm-dd'),50.0); 

1 row created. 

SQL> insert into transactions values ('B',to_date('2008-02-01','yyyy-mm-dd'),50.0); 

1 row created. 

SQL> insert into transactions values ('B',to_date('2009-08-01','yyyy-mm-dd'),5.0); 

1 row created. 

對於分區外部聯接你只需要一組幾年來劃分外連接反對。在下面的查詢中,我使用了2年(2008年和2009年),但您可以輕鬆調整該設置。

SQL> with the_years as 
    2 (select 2007 + level year 
    3   , trunc(to_date(2007 + level,'yyyy'),'yy') start_of_year 
    4   , trunc(to_date(2007 + level + 1,'yyyy'),'yy') - interval '1' second end_of_year 
    5  from dual 
    6 connect by level <= 2 
    7 ) 
    8 select t.category    "Category" 
    9  , y.year     "Year" 
10  , nvl(sum(t.trans_value),0) "sum" 
11 from the_years y 
12   left outer join transactions t 
13   partition by (t.category) 
14   on (t.trans_date between y.start_of_year and y.end_of_year) 
15 group by t.category 
16  , y.year 
17 order by t.category 
18  , y.year 
19/

Category  Year  sum 
-------- ---------- ---------- 
A    2008  200 
A    2009   0 
B    2008  100 
B    2009   5 

4 rows selected. 

另外請注意,我用start_of_year和END_OF_YEAR,所以如果你想在TRANS_DATE過濾,你必須對列的索引,它可以被使用。另一個選擇是簡單地使用trunc(t.trans_date)= y.year作爲條件。

希望這會有所幫助。

Regards,
Rob。

+0

我使用了分區外連接。非常感謝你。 – sysoutkoula

0

下面是一個完整,工作示例:

CREATE TABLE transactions (CATEGORY VARCHAR(1), trans_date DATE, trans_value NUMBER(25,8)); 
CREATE TABLE YEAR (YEAR NUMBER(4)); 

CREATE TABLE categories (CATEGORY VARCHAR(1)); 
INSERT INTO categories VALUES ('A'); 
INSERT INTO categories VALUES ('B'); 

INSERT INTO transactions VALUES ('A',to_date('2008-01-01','YYYY-MM-DD'),100.0); 
INSERT INTO transactions VALUES ('A',to_date('2008-02-01','YYYY-MM-DD'),100.0); 

INSERT INTO transactions VALUES ('B',to_date('2008-01-01','YYYY-MM-DD'),50.0); 
INSERT INTO transactions VALUES ('B',to_date('2008-02-01','YYYY-MM-DD'),50.0); 

INSERT INTO transactions VALUES ('B',to_date('2009-08-01','YYYY-MM-DD'),5.0); 

INSERT INTO YEAR VALUES (2008); 
INSERT INTO YEAR VALUES (2009); 


SELECT b.category 
    , b.year 
    , SUM(nvl(a.trans_value,0)) 
    FROM (SELECT to_char(a.trans_date,'YYYY') YEAR 
      , CATEGORY 
      , SUM(NVL(trans_value,0)) trans_value 
      FROM transactions a 
     GROUP BY to_char(a.trans_date,'YYYY') 
       , a.category) a 
    , (SELECT 
     DISTINCT a.category 
       , b.year 
      FROM categories a 
      , YEAR b) b 
WHERE b.year = to_char(a.year(+)) 
    AND b.category = a.category(+) 
GROUP BY 
     b.category 
    , b.year 
ORDER BY 1 
     ,2; 

輸出:

CATEGORY YEAR SUM(NVL(A.TRANS_VALUE,0)) 
1 A   2008 200 
2 A   2009 0 
3 B   2008 100 
4 B   2009 5 
+0

但是例如,對於類別A,在2008年沒有行具有trans_date以取得總和。 – sysoutkoula

+0

@Koula - 查看我的最新編輯,我提供了一個工作示例。 – dcp

1

您最好需要類別的表和年表:

select c.category, y.year, nvl(sum(t.trans_value),0) 
from categories c 
cross join years y 
left outer join transaction t 
    on to_char(t.trans_date, 'YYYY') = y.year 
    and t.category = c.category 
group by c.category, y.year 
order by 1, 2; 

希望你有一張ca tegories,但你可能不具有多年的表,在這種情況下,你可以「假」一個這樣的:

with years as 
(select 2007+rownum year 
    from dual 
    connect by rownum < 10) -- returns 2008, 2009, ..., 2017 
select c.category, y.year, nvl(sum(t.trans_value),0) 
from categories c 
cross join years y 
left outer join transaction t 
    on to_char(t.trans_date, 'YYYY') = y.year 
    and t.category = c.category 
group by c.category, y.year 
order by 1, 2;