Der Datentyp der Spalte people
ist json
, wie das Ergebnis von json_array_elements(people)
. Und es gibt keinen Gleichheitsoperator ( =
) für den Datentyp json
. Sie können also auch nicht darauf laufen GROUP BY
. Mehr:
jsonb
hat einen Gleichheitsoperator, daher besteht die "Problemumgehung" in Ihrer Antwort darin, jsonb
das Äquivalent zu verwenden und zu verwenden jsonb_array_elements()
. Die Besetzung erhöht die Kosten:
jsonb_array_elements(people::jsonb)
Seit Postgres 9.4 haben wir auch json_array_elements_text(json)
Array-Elemente als zurückgegeben text
. Verbunden:
Damit:
SELECT p.name, count(*) AS c
FROM band b, json_array_elements_text(b.people) p(name)
GROUP BY p.name;
Es scheint bequemer zu sein, Namen text
anstelle von jsonb
Objekten zu erhalten (in Textdarstellung in doppelten Anführungszeichen), und Ihre "gewünschte Ausgabe" zeigt an, dass Sie text
im Ergebnis zunächst wollen / brauchen .
GROUP BY
on text
data ist auch billiger als on jsonb
, daher sollte diese alternative "Problemumgehung" aus zwei Gründen schneller sein. (Test mit EXPLAIN (ANALYZE, TIMING OFF)
.)
Für die Aufzeichnung war nichts falsch mit Ihrer ursprünglichen Antwort . Das Komma ( ,
) ist genauso "korrekt" wie CROSS JOIN LATERAL
. Eine frühere Definition in Standard-SQL macht es nicht minderwertig. Sehen:
Es ist auch nicht portabler für andere RDBMS, und da es zunächst nicht für andere RDBMS portierbar ist jsonb_array_elements()
oder json_array_elements_text()
nicht, ist dies auch irrelevant. Die kurze Abfrage wird mit CROSS JOIN LATERAL
IMO nicht klarer , aber das letzte Stück ist nur meine persönliche Meinung.
Ich habe den expliziteren Tabellen- und Spaltenalias p(name)
und die tabellenqualifizierte Referenz verwendet p.name
, um mich gegen mögliche doppelte Namen zu verteidigen. name
ist ein so gebräuchliches Wort, dass es möglicherweise auch als Spaltenname in der zugrunde liegenden Tabelle angezeigt band
wird. In diesem Fall wird es stillschweigend aufgelöst band.name
. Das einfache Formular json_array_elements_text(people) name
fügt nur einen Tabellenalias hinzu , der Spaltenname bleibt value
wie von der Funktion zurückgegeben. Aber name
löst es die einzige Spalte , value
wenn in der verwendeten SELECT
Liste. Es funktioniert wie erwartet . Aber ein wahrer Spaltenname name
(falls band.name
vorhanden) würde zuerst binden. Während das im gegebenen Beispiel nicht beißt, kann es in anderen Fällen eine geladene Fußwaffe sein.
Verwenden Sie zunächst nicht den generischen "Namen" als Kennung. Vielleicht war das nur für den einfachen Testfall.
Wenn die Spalte people
alles andere als ein einfaches JSON-Array enthalten kann , würde jede Abfrage eine Ausnahme auslösen. Wenn Sie die Datenintegrität nicht garantieren können, möchten Sie möglicherweise Folgendes verteidigen 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
Schließt verletzende Zeilen von der Abfrage aus.
Verbunden: