2011-07-05 57 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 | ...

唯一的区别是:一切都包含在一个列中。当然,这个单列是一个数组,每个元素都是一个复合类型,如果你将这个结构扁平化,你就得到了你要求的东西。当然,这取决于你是否有手段进行扁平化。