如何将array_agg作为迭代列循环?

时间:2011-07-05 19:26:03

标签: sql postgresql

使用PostgreSQL 8.4,我已经成功地使用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}"  

如果我对每次迭代进行硬编码,我可以抓取数组的一部分来创建新列:

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. 循环浏览记录,这样我就不必创建字段的每次迭代。好消息是上面的结构没有错误,只是传递NULL,但如果我得到一些疯狂的记录数,我不必在我的声明中手动创建order_55,order_56等。

  2. 更好的是最终不会将其传递给特定字段并让它遍历所有字段(除了customer_id)并给我每个字段的迭代,以达到以下效果:

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

    等等。基本上将父表(客户)连接到子(订单),但是有多个子记录跨越单行而不是创建倍数。

    (是的,我理解这与你为什么首先做父/子表的基本概念背道而驰。但是,我将它传递给客户端,这将使这个过程变得更加容易。)

  3. 更新:我已经接近了相似的功能......第一次调用它时,它会创建列并填充其中一个客户。但由于某种原因,我的IF NOT EXISTS在单独运行时起作用,但不在函数内:我得到“列order_id1存在”错误。我也想最终修改它,因此特定字段不是硬编码的;而不是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;
    

    我为在地图上的所有人道歉,因为我一直试图解决有关此问题的一些事情,我无法理解。感谢任何帮助,谢谢!

2 个答案:

答案 0 :(得分: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  

答案 1 :(得分: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\")"}"

这看起来非常接近你所说的#34;甚至更好":

  

客户| order_id1 | order_date1 | order_desc1 | order_id2 | order_date2 | order_desc2 |   ...

唯一的区别是:所有内容都包含在一个列中。当然,单个列是一个数组,每个元素都是一个复合类型,如果你展平那个结构,你就得到了你所要求的。如果当然这取决于你是否有办法进行压扁。