循环选择查询

时间:2014-08-20 09:41:54

标签: sql postgresql pivot aggregate-functions crosstab

我想做这样的事情:

select id,
    count(*) as total,
    FOR temp IN SELECT DISTINCT somerow FROM mytable ORDER BY somerow LOOP
    sum(case when somerow = temp then 1 else 0 end) temp,
    END LOOP;
from mytable
group by id
order by id

我创建了工作选择:

select id,
    count(*) as total,
    sum(case when somerow = 'a' then 1 else 0 end) somerow_a,
    sum(case when somerow = 'b' then 1 else 0 end) somerow_b,
    sum(case when somerow = 'c' then 1 else 0 end) somerow_c,
    sum(case when somerow = 'd' then 1 else 0 end) somerow_d,
    sum(case when somerow = 'e' then 1 else 0 end) somerow_e,
    sum(case when somerow = 'f' then 1 else 0 end) somerow_f,
    sum(case when somerow = 'g' then 1 else 0 end) somerow_g,
    sum(case when somerow = 'h' then 1 else 0 end) somerow_h,
    sum(case when somerow = 'i' then 1 else 0 end) somerow_i,
    sum(case when somerow = 'j' then 1 else 0 end) somerow_j,
    sum(case when somerow = 'k' then 1 else 0 end) somerow_k 
from mytable
group by id
order by id

这是有效的,但它是'静态' - 如果将某些新值添加到'somerow',我将不得不手动更改sql以从somerow列获取所有值,这就是为什么我想知道它是否可以用for循环做一些事情。

所以我想得到的是:

id    somerow_a    somerow_b    ....
0     3            2            ....
1     2            10           ....
2     19           3            ....
.     ...          ...
.     ...          ...
.     ...          ...

所以我想做的是计算所有包含特定字母的行并按id分组(此id不是主键,但它重复 - 对于id大约有80种不同价值可能)。

http://sqlfiddle.com/#!15/18feb/2

2 个答案:

答案 0 :(得分:3)

阵列对你有好处吗? (SQL Fiddle)

select
    id,
    sum(totalcol) as total,
    array_agg(somecol) as somecol,
    array_agg(totalcol) as totalcol
from (
    select id, somecol, count(*) as totalcol
    from mytable
    group by id, somecol
) s
group by id
;
 id | total | somecol | totalcol 
----+-------+---------+----------
  1 |     6 | {b,a,c} | {2,1,3}
  2 |     5 | {d,f}   | {2,3}

在9.2中,可以有一组JSON对象(Fiddle)

select row_to_json(s)
from (
    select
        id,
        sum(totalcol) as total,
        array_agg(somecol) as somecol,
        array_agg(totalcol) as totalcol
    from (
        select id, somecol, count(*) as totalcol
        from mytable
        group by id, somecol
    ) s
    group by id
) s
;
                          row_to_json                          
---------------------------------------------------------------
 {"id":1,"total":6,"somecol":["b","a","c"],"totalcol":[2,1,3]}
 {"id":2,"total":5,"somecol":["d","f"],"totalcol":[2,3]}

在9.3中,添加了lateral,单个对象(Fiddle)

select to_json(format('{%s}', (string_agg(j, ','))))
from (
    select format('%s:%s', to_json(id), to_json(c)) as j
    from
        (
            select
                id,
                sum(totalcol) as total_sum,
                array_agg(somecol) as somecol_array,
                array_agg(totalcol) as totalcol_array
            from (
                select id, somecol, count(*) as totalcol
                from mytable
                group by id, somecol
            ) s
            group by id
        ) s
        cross join lateral
        (
            select
                total_sum as total,
                somecol_array as somecol,
                totalcol_array as totalcol
        ) c
) s
;
                                                                to_json                                                                
---------------------------------------------------------------------------------------------------------------------------------------
 "{1:{\"total\":6,\"somecol\":[\"b\",\"a\",\"c\"],\"totalcol\":[2,1,3]},2:{\"total\":5,\"somecol\":[\"d\",\"f\"],\"totalcol\":[2,3]}}"

在9.2中,也可以使用子查询而不是lateral

以更复杂的方式拥有单个对象

答案 1 :(得分:1)

SQL对返回类型非常严格。它需要事先知道要返回什么。

对于完全动态数量的结果值,您只能使用@Clodoaldo posted之类的数组。实际上,静态返回类型不会为每个值获取单独的列。

如果您知道通话时的列数("半动态" ),您可以创建一个获取(并返回)多态参数的函数。与许多细节密切相关的答案:

(您还可以在那里找到@Clodoaldo数组的相关答案。)

您剩下的选择是对服务器使用两次往返。第一个用实际返回类型确定实际查询。第二个是根据第一次调用执行查询。

否则,您必须使用静态查询。在这样做的同时,我现在看到了两个更好的选项

1。更简单的表达

select id
     , count(*) AS total
     , count(somecol = 'a' OR NULL) AS somerow_a
     , count(somecol = 'b' OR NULL) AS somerow_b
     , ...
from   mytable
group  by id
order  by id;

它是如何运作的?

SQL Fiddle.

2。 crosstab()

crosstab()起初更复杂,但用C语言编写,针对任务进行了优化,对于长列表则更短。您需要安装附加模块tablefunc。如果您不熟悉,请阅读此处的基础知识:

SELECT * FROM crosstab(
   $$
   SELECT id
        , count(*) OVER (PARTITION BY id)::int AS total
        , somecol
        , count(*)::int AS ct  -- casting to int, don't think you need bigint?
   FROM   mytable
   GROUP  BY 1,3
   ORDER  BY 1,3
   $$
   ,
   $$SELECT unnest('{a,b,c,d}'::text[])$$
   ) AS f (id int, total int, a int, b int, c int, d int);