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 BYisso. Mais:
jsonbpossui um operador de igualdade, portanto, a "solução alternativa" em sua resposta é converter jsonbe 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 textvez de jsonbobjetos (aspas duplas na representação de texto) e sua "saída desejada" indica que você deseja / precisa textno resultado para começar.
GROUP BYnos textdados 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 LATERALIMO, 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.namepara 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) nameanexa apenas um alias da tabela ; o nome da coluna ainda é value, conforme retornado da função. Mas nameresolve para sua única coluna valuequando usado na SELECTlista. Ele passa a trabalhar como esperado . Mas um nome de coluna verdadeiro name(se band.nameexistir) 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 peoplepuder 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: