Looping en la consulta de selección

Quiero hacer algo como esto:

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 

Creé seleccionar trabajo:

 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 

esto funciona, pero es 'estático' – si se agrega un nuevo valor a 'somerow' tendré que cambiar sql manualmente para get todos los valores de la columna somerow, y es por eso que me pregunto si es posible hacer algo con for loop.

Entonces, lo que quiero get es esto:

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

Entonces, lo que me gustaría hacer es contar todas las filas que tengan una letra específica y agruparla por id (esta identificación no es key principal, pero se repite, para ID hay alnetworkingedor de 80 valores diferentes) .

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

¿Las matrices son buenas para ti? (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} 

En 9.2 es posible tener un set de objects 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]} 

En 9.3, con la adición de lateral , un solo object (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]}}" 

En 9.2 también es posible tener un solo object de una manera más intrincada utilizando subconsultas en lugar de lateral

SQL es muy rígido sobre el tipo de devolución. Exige saber qué devolver de antemano.

Para una cantidad completamente dinámica de valores resultantes, solo puede usar matrices como @Clodoaldo publicado . Efectivamente, un tipo de retorno estático, no obtiene columnas individuales para cada valor.

Si conoce el número de columnas en el time de llamada ( "semidynamic" ), puede crear una function que tome (y regrese) parameters polimórficos. Respuesta estrechamente relacionada con muchos detalles:

  • Alternativa dinámica para pivotar con CASE y GROUP BY

(También encontrará una respuesta relacionada con matrices de @Clodoaldo allí).

Su opción restante es usar dos viajes de ida y vuelta al server. El primero en determinar la consulta real con el tipo de devolución real. El segundo para ejecutar la consulta en function de la primera llamada.

De lo contrario, tienes que ir con una consulta estática . Mientras hago eso, veo dos opciones más agradables para lo que tienes ahora:

1. Expresión más simple

 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; 

¿Como funciona?

  • Calcular porcentajes de SUM () en la misma consulta SELECT sql

SQL Fiddle.

2. crosstab()

crosstab() es más compleja al principio, pero está escrita en C, optimizada para la tarea y más corta para las lists largas. Necesita el module adicional tablefunc instalado. Lea los conceptos básicos aquí si no está familiarizado:

  • Consulta de tabla cruzada de PostgreSQL

 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);