El tipo de datos de la columna people
es json
, como es el resultado de json_array_elements(people)
. Y no hay operador de igualdad ( =
) para el tipo de datos json
. Entonces tampoco puedes ejecutarlo GROUP BY
. Más:
jsonb
tiene un operador de igualdad, por lo que la "solución" en su respuesta es emitir jsonb
y usar el equivalente jsonb_array_elements()
. El elenco agrega costo:
jsonb_array_elements(people::jsonb)
Desde Postgres 9.4 también tenemos json_array_elements_text(json)
elementos de matriz de retorno como text
. Relacionado:
Entonces:
SELECT p.name, count(*) AS c
FROM band b, json_array_elements_text(b.people) p(name)
GROUP BY p.name;
Parece más conveniente obtener nombres en text
lugar de jsonb
objetos (entre comillas dobles en la representación de texto) y su "resultado deseado" indica que quiere / necesita text
en el resultado para empezar.
GROUP BY
on text
data también es más barato que on jsonb
, por lo que esta "solución" alternativa debería ser más rápida por dos razones. (Prueba con EXPLAIN (ANALYZE, TIMING OFF)
.)
Para el registro, no hubo nada malo con su respuesta original . La coma ( ,
) es tan "correcta" como CROSS JOIN LATERAL
. Haber sido definido anteriormente en SQL estándar no lo hace inferior. Ver:
Tampoco es más portátil para otros RDBMS, y dado que jsonb_array_elements()
o json_array_elements_text()
no son portátiles para otros RDBMS, eso también es irrelevante. La breve consulta no se aclara con CROSS JOIN LATERAL
IMO, pero el último bit es solo mi opinión personal.
Utilicé el alias de tabla y columna más explícito p(name)
y la referencia calificada de tabla p.name
para defenderme de posibles nombres duplicados. name
es una palabra tan común, también puede aparecer como nombre de columna en la tabla subyacente band
, en cuyo caso se resolvería en silencio band.name
. La forma simple json_array_elements_text(people) name
solo adjunta un alias de tabla , el nombre de la columna sigue siendo value
, tal como lo devuelve la función. Pero name
resuelve su columna única value
cuando se usa en la SELECT
lista. Se pasa a trabajar como se esperaba . Pero un verdadero nombre de columna name
(si band.name
debería existir) se uniría primero. Si bien eso no morderá en el ejemplo dado, puede ser una pistola de pie cargada en otros casos.
Para empezar, no use el "nombre" genérico como identificador. Tal vez eso fue solo por el simple caso de prueba.
Si la columna people
puede contener cualquier cosa menos una matriz JSON simple , cualquiera de las consultas desencadenará una excepción. Si no puede garantizar la integridad de los datos, puede defender con json_typeof()
:
SELECT p.name, count(*) AS c
FROM band b, json_array_elements_text(b.people) p(name)
WHERE json_typeof(b.people) = 'array'
GROUP BY 1; -- optional short syntax since you seem to prefer short syntax
Excluye las filas infractoras de la consulta.
Relacionado: