2011-07-05 60 views
3

使用PostgreSQL 8.4,我已經成功地能夠使用ARRAY_AGG()取多個訂單的情況下,讓每個客戶單行:如何通過array_agg遍歷迭代列?

從這:

order_id|customer_id|order_date |order_desc 
1  |1   |"2010-01-01"|"Tom's First" 
2  |1   |"2010-04-01"|"Tom's Second" 
7  |1   |"2010-04-13"|"Tom's Third" 
8  |1   |"2011-04-13"|"Tom's Last" 
5  |1   |"2011-06-20"|"Tom's Really Last." 
3  |2   |"2010-07-07"|"Dick's First" 
6  |2   |"2011-07-07"|"Dick's Other" 
4  |3   |"2011-04-04"|"Harry's Only" 

使用此:

select cu.customer, array_agg(ord.order_id) as orders from test_order ord 
inner join test_customer cu 
on ord.customer_id = cu.customer_id 
group by cu.customer 

結果:

customer |orders 
"Tom"  |"{1,2,7,8,5}" 
"Dick"  |"{3,6}" 
"Harry" |"{4}" 

的d我可以抓住陣列片創建新列,如果我硬代碼每個迭代:

select cu.customer, 
(array_agg(ord.order_id))[1] as order_1, 
(array_agg(ord.order_id))[2] as order_2, 
(array_agg(ord.order_id))[3] as order_3, 
(array_agg(ord.order_id))[4] as order_4, 
(array_agg(ord.order_id))[5] as order_5 
from test_order ord 
inner join test_customer cu 
on ord.customer_id = cu.customer_id 
group by cu.customer 

結果:

customer|order_1|order_2|order_3|order_4|order_5 
"Dick" |3  |6  |  |  | 
"Harry" |4  |  |  |  | 
"Tom" |8  |1  |5  |2  |7 

然而,想什麼,我做的,在兩個步驟:

  1. For循環我的方式通過記錄,以便我不必創建該字段的每個迭代。好消息是上面的結構沒有錯誤,只是傳遞NULL,但是如果我遇到了一些瘋狂的記錄,我不必在我的聲明中手動創建order_55,order_56等。

  2. 更妙的是最終沒有通過其特定的領域,有它在所有的領域進行迭代(除非該CUSTOMER_ID),給我的每場的迭代,該效果:

    customer|order_id1|order_date1|order_desc1|order_id2|order_date2|order_desc2| ... 
    

    等等基本上加入父表(客戶)的子(訂單),但有多個子記錄跨越一行而不是創建倍數。

    (是的,我知道這與您爲什麼首先執行父/子表的基本概念相反,不過,我將此傳遞給客戶端,這將使該過程變得更加容易。)

更新:我已經得到了與拆解功能更接近......我第一次稱呼它,它創建的列和填充的客戶之一。但由於某些原因,我的IF NOT EXISTS在單獨運行時工作,但不在函數內:我得到「column order_id1 exists」錯誤。我也想最終修改這個,以便特定的字段不被硬編碼;而不是customer_id我想要做一些事情,比如傳遞父表,子表和連接ID,並且用這種交叉表方式完全追加子表。

CREATE FUNCTION loop_test(integer) RETURNS integer AS $$ 

DECLARE 
rOrder RECORD; 
loop_counter INT := 1; 
target_customer_id ALIAS FOR $1;   
BEGIN 

FOR rOrder IN SELECT * 
    FROM vdad_data.test_order 
    WHERE customer_id = target_customer_id 
    ORDER BY order_id LOOP 

    IF NOT EXISTS 
     (
     SELECT * FROM information_schema.COLUMNS 
     WHERE COLUMN_NAME= 'order_id' || loop_counter 
     AND TABLE_NAME='test_customer' 
     AND TABLE_SCHEMA='vdad_data' 
     ) 
     THEN 

     EXECUTE 'ALTER TABLE vdad_data.test_customer 
     ADD COLUMN order_id' || loop_counter || ' integer'; 
    END IF; 

    IF NOT EXISTS 
     (
     SELECT * FROM information_schema.COLUMNS 
     WHERE COLUMN_NAME= 'order_date' || loop_counter 
     AND TABLE_NAME='test_customer' 
     AND TABLE_SCHEMA='vdad_data' 
     ) 
     THEN 

     EXECUTE 'ALTER TABLE vdad_data.test_customer 
     ADD COLUMN order_date' || loop_counter || ' date'; 
    END IF; 


    IF NOT EXISTS 
     (
     SELECT * FROM information_schema.COLUMNS 
     WHERE COLUMN_NAME= 'order_desc' || loop_counter 
     AND TABLE_NAME='test_customer' 
     AND TABLE_SCHEMA='vdad_data' 
     ) 
     THEN 

     EXECUTE 'ALTER TABLE vdad_data.test_customer 
     ADD COLUMN order_desc' || loop_counter || ' character varying'; 
    END IF; 

EXECUTE 'UPDATE vdad_data.test_customer 
     SET order_id' || loop_counter || ' = ' || rOrder.order_id ||', 
     order_date' || loop_counter || ' = ' || quote_literal(to_char(rOrder.order_date,'yyyy-mm-dd')) ||',   
     order_desc' || loop_counter || ' = ' || quote_literal(rOrder.order_desc) ||' 
     WHERE customer_id = ' ||rOrder.customer_id; 

loop_counter = loop_counter + 1; 
END LOOP; 

RETURN 1; 
END; 
$$ LANGUAGE plpgsql; 

我爲是所有在地圖上道歉,因爲我一直在嘗試了一次,我不能完全得到解決這個幾件事情。任何幫助表示讚賞,謝謝!

+1

您是否想要做一些類似[this crosstab](http://www.postgresql.org/docs/current/static/tablefunc.html#AEN126191)的函數? –

+0

人。有點。我猜這是一個9.0的功能,因爲我沒有在8.4。但似乎仍然需要每個領域的手動硬編碼?並且每個數據字段的標題不是由第三列(該示例中的「cat」值)確定的,它只是現有列標題的迭代:order_id變爲order_id1,order_id2等。 – MitchO

+1

@MitchO:它也在8.4。您需要安裝contrib。 –

回答

1

您是否試圖獲取每個客戶訂單的序號?你可以用Postgres 8.4中的row_number()函數來做到這一點。爲每個訂單號創建單獨的列在SQL中不可持續或高效。

喜歡的東西:

select cu.customer, 
     row_number() OVER(PARTITION BY cu.customer ORDER BY ord.order_date) 
from test_order ord inner join test_customer cu 
    on ord.customer_id = cu.customer_id 
group by cu.customer 
+0

不,這不是我想要做的。不幸的是,這是我真實數據的簡化版本。你可能會覺得單獨的列效率不高,但是我有一種情況,即以多表格格式獲取數據,並且我需要將它作爲單個表格提供。因此,如果我只是做了一個標準的連接,我會得到一個數量過多的增加的行。想象一下客戶的一張主表,另一個訂單,另一個地址,另一個親戚......以及每個客戶的加入。 – MitchO

+0

我得到了正確的數據,但是我可以將一個客戶變成數百個,如果不是數千個記錄,並且它會比order1,order2,address1,address2,addresss3,relative1等更好地服務。等等。 – MitchO

+0

對不起,我猜猜你的應用程序試圖做什麼並不完全清楚。如果您有多個(多個地址或訂單中的一個客戶)加入,則解決方案的一種方法是編寫兩個查詢。列出所有客戶地址和列出所有訂單的地址。如果您嘗試將一個客戶加入到這兩個地址和訂單中,就會得到您所描述的交叉連接情況(我認爲)。 –

0

此:

select array_agg(row(order_id,order_date,order_desc)) 
from (
select 1 order_id,1 customer_id,'2010-01-01' order_date,'Tom''s First' order_desc union 
select 2 order_id,1 customer_id,'2010-04-01' order_date,'Tom''s Second' order_desc union 
select 7 order_id,1 customer_id,'2010-04-13' order_date,'Tom''s Third' order_desc union 
select 8 order_id,1 customer_id,'2011-04-13' order_date,'Tom''s Last' order_desc union 
select 5 order_id,1 customer_id,'2011-06-20' order_date,'Tom''s Really Last.' order_desc union 
select 3 order_id,2 customer_id,'2010-07-07' order_date,'Dick''s First' order_desc union 
select 6 order_id,2 customer_id,'2011-07-07' order_date,'Dick''s Other' order_desc union 
select 4 order_id,3 customer_id,'2011-04-04' order_date,'Harry''s Only' order_desc 
) orders 
group by orders.customer_id 

給你三排:

"{"(2,2010-04-01,\"Tom's Second\")","(1,2010-01-01,\"Tom's First\")","(7,2010-04-13,\"Tom's Third\")","(5,2011-06-20,\"Tom's Really Last.\")","(8,2011-04-13,\"Tom's Last\")"}" 

"{"(3,2010-07-07,\"Dick's First\")","(6,2011-07-07,\"Dick's Other\")"}" 

"{"(4,2011-04-04,\"Harry's Only\")"}" 

這看起來非常接近,你說什麼是 「更好」 :

customer | order_id1 | order_date1 | order_desc1 | order_id2 | order_date2 | order_desc2 | ...

唯一的區別是:一切都包含在一個列中。當然,這個單列是一個數組,每個元素都是一個複合類型,如果你將這個結構扁平化,你就得到了你要求的東西。當然,這取決於你是否有手段進行扁平化。