O tipo de dados da coluna people
é json
, como é o resultado de json_array_elements(people)
. E não há operador de igualdade ( =
) para o tipo de dados json
. Então você também não pode executar GROUP BY
isso. Mais:
jsonb
possui um operador de igualdade, portanto, a "solução alternativa" em sua resposta é converter jsonb
e usar o equivalente jsonb_array_elements()
. O elenco acrescenta custo:
jsonb_array_elements(people::jsonb)
Desde o Postgres 9.4, também temos json_array_elements_text(json)
elementos de matriz retornados como text
. Relacionado:
Assim:
SELECT p.name, count(*) AS c
FROM band b, json_array_elements_text(b.people) p(name)
GROUP BY p.name;
Parece mais conveniente obter nomes como em text
vez de jsonb
objetos (aspas duplas na representação de texto) e sua "saída desejada" indica que você deseja / precisa text
no resultado para começar.
GROUP BY
nos text
dados também é mais barato do que nos jsonb
, portanto, essa "solução alternativa" deve ser mais rápida por dois motivos. (Teste com EXPLAIN (ANALYZE, TIMING OFF)
.)
Para o registro, não havia nada errado com sua resposta original . A vírgula ( ,
) é tão "correta" quanto CROSS JOIN LATERAL
. Tendo sido definido anteriormente no SQL padrão, não o torna inferior. Vejo:
Também não é mais portável para outros RDBMS, e uma vez que jsonb_array_elements()
ou json_array_elements_text()
não são portáveis para outros RDBMS, para começar, que também é irrelevante. A consulta curta não fica mais clara com a CROSS JOIN LATERAL
IMO, mas a última parte é apenas minha opinião pessoal.
Usei o alias de tabela e coluna mais explícito p(name)
e a referência qualificada p.name
para se defender contra possíveis nomes duplicados. name
é uma palavra tão comum, também pode aparecer como nome da coluna na tabela subjacente band
; nesse caso, ela seria resolvida silenciosamente band.name
. O formulário simples json_array_elements_text(people) name
anexa apenas um alias da tabela ; o nome da coluna ainda é value
, conforme retornado da função. Mas name
resolve para sua única coluna value
quando usado na SELECT
lista. Ele passa a trabalhar como esperado . Mas um nome de coluna verdadeiro name
(se band.name
existir) seria vinculado primeiro. Enquanto isso não vai morder no exemplo dado, pode ser uma arma de pé carregada em outros casos.
Não use o "nome" genérico como identificador para começar. Talvez fosse apenas para o caso de teste simples.
Se a coluna people
puder conter algo além de uma matriz JSON simples , qualquer uma dessas consultas acionará uma exceção. Se você não pode garantir a integridade dos dados, convém se defender com 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
Exclui violar linhas da consulta.
Relacionado: