Verwenden Sie RETURN QUERY
:
CREATE OR REPLACE FUNCTION word_frequency(_max_tokens int)
RETURNS TABLE (txt text -- also visible as OUT parameter inside function
, cnt bigint
, ratio bigint)
LANGUAGE plpgsql AS
$func$
BEGIN
RETURN QUERY
SELECT t.txt
, count(*) AS cnt -- column alias only visible inside
, (count(*) * 100) / _max_tokens -- I added brackets
FROM (
SELECT t.txt
FROM token t
WHERE t.chartype = 'ALPHABETIC'
LIMIT _max_tokens
) t
GROUP BY t.txt
ORDER BY cnt DESC; -- potential ambiguity
END
$func$;
Aufruf:
SELECT * FROM word_frequency(123);
Das explizite Definieren des Rückgabetyps ist viel praktischer als die Rückgabe eines generischen record
. Auf diese Weise müssen Sie nicht bei jedem Funktionsaufruf eine Spaltendefinitionsliste bereitstellen. RETURNS TABLE
ist eine Möglichkeit, dies zu tun. Da sind andere. Datentypen von OUT
Die Parameter müssen genau mit dem übereinstimmen, was von der Abfrage zurückgegeben wird.
Wählen Sie Namen für OUT
Parameter sorgfältig. Sie sind im Funktionskörper fast überall sichtbar. Tabellenqualifizieren Sie Spalten mit demselben Namen, um Konflikte oder unerwartete Ergebnisse zu vermeiden. Ich habe das für alle Spalten in meinem Beispiel gemacht.
Beachten Sie jedoch den potenziellen Namenskonflikt zwischen OUT
Parameter cnt
und der gleichnamige Spaltenalias. In diesem speziellen Fall (RETURN QUERY SELECT ...
) Postgres verwendet den Spaltenalias über OUT
Parameter so oder so. Dies kann jedoch in anderen Zusammenhängen mehrdeutig sein. Es gibt verschiedene Möglichkeiten, Verwirrung zu vermeiden:
- Verwenden Sie die Ordnungsposition des Elements in der SELECT-Liste:
ORDER BY 2 DESC
. Beispiel:- Erste Zeile in jeder GROUP BY-Gruppe auswählen?
- Wiederholen Sie den Ausdruck
ORDER BY count(*)
. - (Hier nicht zutreffend.) Setzen Sie den Konfigurationsparameter
plpgsql.variable_conflict
oder verwenden Sie den speziellen Befehl#variable_conflict error | use_variable | use_column
in der Funktion. Siehe:- Namenskonflikt zwischen Funktionsparameter und Ergebnis von JOIN mit USING-Klausel
Verwenden Sie nicht „text“ oder „count“ als Spaltennamen. Beide können in Postgres legal verwendet werden, aber "count" ist ein reserviertes Wort in Standard-SQL und ein grundlegender Funktionsname und "Text" ist ein grundlegender Datentyp. Kann zu verwirrenden Fehlern führen. Ich verwende txt
und cnt
In meinen Beispielen möchten Sie vielleicht deutlichere Namen.
Fehlender ;
hinzugefügt und einen Syntaxfehler im Header korrigiert. (_max_tokens int)
, nicht (int maxTokens)
- tippen nach Name .
Beim Arbeiten mit ganzzahliger Division ist es besser, zuerst zu multiplizieren und später zu dividieren, um den Rundungsfehler zu minimieren. Oder arbeiten Sie mit numeric
oder ein Fließkommatyp. Siehe unten.
Alternative
Das ist, was ich denke Ihre Abfrage sollte tatsächlich so aussehen (Berechnung eines relativen Anteils pro Token ):
CREATE OR REPLACE FUNCTION word_frequency(_max_tokens int)
RETURNS TABLE (txt text
, abs_cnt bigint
, relative_share numeric)
LANGUAGE plpgsql AS
$func$
BEGIN
RETURN QUERY
SELECT t.txt, t.cnt
, round((t.cnt * 100) / (sum(t.cnt) OVER ()), 2) -- AS relative_share
FROM (
SELECT t.txt, count(*) AS cnt
FROM token t
WHERE t.chartype = 'ALPHABETIC'
GROUP BY t.txt
ORDER BY cnt DESC
LIMIT _max_tokens
) t
ORDER BY t.cnt DESC;
END
$func$;
Der Ausdruck sum(t.cnt) OVER ()
ist eine Fensterfunktion. Sie könnten Verwenden Sie anstelle der Unterabfrage einen CTE. Hübsch, aber eine Unterabfrage ist in einfachen Fällen wie diesem normalerweise billiger (meistens vor Postgres 12).
Ein abschließendes explizites RETURN
Aussage ist nicht erforderlich (aber erlaubt), wenn mit OUT
gearbeitet wird Parameter oder RETURNS TABLE
(was OUT
implizit verwendet Parameter).
round()
mit zwei Parametern funktioniert nur für numeric
Typen. count()
in der Unterabfrage erzeugt ein bigint
result und eine sum()
über diesen bigint
erzeugt einen numeric
Ergebnis, wir haben es also mit einem numeric
zu tun Nummer automatisch und alles fügt sich einfach zusammen.