2013-06-05 30 views
0

我正在做一些關於財務賬戶的報告,並且我需要根據初始數字對數值進行求和...... 例如,將所有以0開頭的賬戶值(01,011 ,012。),或從1開始(1,10,111 ...),或與111(111,1112,1113 ...)等PostrgreSQL 8.4:通過賬戶號碼求和

這裏開始被簡化試樣臺:

CREATE TABLE account(id, acctNo, credit) AS (
    VALUES 
     (1, '01', 100) 
     ,(2, '011', 200) 
     ,(3, '0112', 300) 
     ,(4, '014', 400) 
     ,(5, '0144', 500) 
     ,(6, '0148', 600) 
     ,(7, '01120', 100) 
     ,(8, '01121', 100) 
     ,(9, '0140', 50) 
     ,(10,'02', 50) 
     ,(11,'021', 50) 
     ,(12,'1', 50) 
     ,(13,'10', 100) 
     ,(15,'100', 50)  
     ,(14,'1021', 50) 
     ,(16,'202', 50) 
     ,(17,'221', 50) 
     ,(18,'4480', 50) 
     ,(19,'447', 50) 
     ,(20,'5880', 50) 
    ) 

我設法做到了,但它有點強大的SQL,有沒有更好的解決方案? 這裏是代碼:

WITH 
    a AS (SELECT SUBSTRING(acctNo,1,1) AS LEVEL, 
    SUM(credit) AS sum1 FROM account GROUP BY LEVEL 
    ORDER BY LEVEL), 
    b AS 
    (SELECT SUBSTRING(acctNo,1,2) AS level2, 
    SUM(credit) FROM account GROUP BY level2 
    ORDER BY level2), 
     c AS 
    (SELECT SUBSTRING(acctNo,1,3) AS level3, 
    SUM(credit) FROM account GROUP BY level3 
    ORDER BY level3), 
    d AS (SELECT SUBSTRING(acctNo,1,4) AS level4, 
    SUM(credit) FROM account GROUP BY level4 
    ORDER BY level4), 
    e AS (SELECT SUBSTRING(acctNo,1,5) AS level5, 
    SUM(credit) FROM account GROUP BY level5 
    ORDER BY level5) 

SELECT * FROM 
(SELECT a.* FROM a 
UNION (SELECT b.* FROM b WHERE char_length(level2)>=2) 
UNION (SELECT c.* FROM c WHERE char_length(level3)>=3) 
UNION (SELECT d.* FROM d WHERE char_length(level4)>=4) 
UNION (SELECT e.* FROM e WHERE char_length(level5)>=5)) a 
ORDER BY LEVEL 

這僅僅是5級(五位數字)......有一些通用的解決方案?如果明天我需要爲6個級別,等什麼...

這裏是SQL Fiddle

感謝。

+0

也許你應該瞭解1NF,第一? – wildplasser

+0

@wildplasser:...也許...謝謝 – lana80

回答

3

Yay generate_series!

SELECT substring(a.acctNo from 1 for g.g) as level, sum(a.credit)  
FROM account a, generate_series(1,5) g 
where length(a.acctNo) >= g.g 
group by 1 
order by 1 
+1

+1。 。 。我非常喜歡這個解決方案,儘管我更喜歡'cross join'在'from'子句中使用','。 –

+1

@GordonLinoff嘿,我剛剛想到這個怪胎:'SELECT substring(a.acctNo從1代表generate_series(1,length(a.acctno)))作爲級別,sum(a.credit)FROM帳號一個組由1'。有點「更清潔」,可能更高性能,但很難理解。 – maniek

+0

@maniek太棒了,非常感謝! – lana80

1

爲了maniek的回答一般:

with levels as (
    select g 
    from generate_series(
     1, 
     (select max(length(acctNo)) from account) 
    ) g 
) 
select substring(acctNo from 1 for l.g) as level, sum(a.credit)  
from account a, levels l 
where length(acctNo) >= l.g 
group by 1 
order by 1 
+0

非常感謝! – lana80