Verwendung 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) 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$ LANGUAGE plpgsql;
Anruf:
SELECT * FROM word_frequency(123);
Erläuterung:
Es ist viel praktischer, den Rückgabetyp explizit zu definieren, als ihn einfach als Datensatz zu deklarieren. Auf diese Weise müssen Sie nicht bei jedem Funktionsaufruf eine Spaltendefinitionsliste bereitstellen. RETURNS TABLE
ist eine Möglichkeit, das zu tun. Da sind andere. Datentypen von OUT
Parametern müssen genau mit dem übereinstimmen, was von der Abfrage zurückgegeben wird.
Wählen Sie die Namen der OUT
Parameter sorgfältig aus. Sie sind im Funktionskörper fast überall sichtbar. Gleichnamige Spalten mit Tabellenqualifizierung, um Konflikte oder unerwartete Ergebnisse zu vermeiden. Ich habe das für alle Spalten in meinem Beispiel gemacht.
Beachten Sie jedoch den möglichen Namenskonflikt zwischen dem OUT
Parameter cnt
und dem gleichnamigen Spaltenalias. In diesem speziellen Fall ( RETURN QUERY SELECT ...
) verwendet Postgres den Spaltenalias über den OUT
Parameter. Dies kann jedoch in anderen Kontexten nicht eindeutig sein. Es gibt verschiedene Möglichkeiten, um Verwirrung zu vermeiden:
- Verwenden Sie die Ordnungsposition des Elements in der SELECT-Liste :
ORDER BY 2 DESC
. Beispiel:
- Wiederholen Sie den Ausdruck
ORDER BY count(*)
.
- (Gilt hier nicht.) Legen Sie den Konfigurationsparameter fest
plpgsql.variable_conflict
oder verwenden Sie den Sonderbefehl #variable_conflict error | use_variable | use_column
in der Funktion. Sehen:
Verwenden Sie nicht "Text" oder "Anzahl" als Spaltennamen. Beide sind in Postgres legal zu verwenden, 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 benutze txt
und cnt
in meinen Beispielen.
Ein fehlender ;
und korrigierter Syntaxfehler im Header wurde hinzugefügt . (_max_tokens int)
, Nicht (int maxTokens)
- geben Sie nach dem Namen .
Bei der Arbeit mit der Ganzzahldivision ist es besser, zuerst zu multiplizieren und später zu dividieren, um den Rundungsfehler zu minimieren. Noch besser: Arbeiten Sie mit numeric
(oder einem Gleitkomma-Typ). Siehe unten.
Alternative
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) 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$ LANGUAGE plpgsql;
Der Ausdruck sum(t.cnt) OVER ()
ist eine Fensterfunktion . Sie könnten einen CTE anstelle der Unterabfrage verwenden - hübsch, aber eine Unterabfrage ist in einfachen Fällen wie diesem normalerweise billiger.
Eine abschließende explizite RETURN
Anweisung ist nicht erforderlich (aber zulässig), wenn mit OUT
Parametern gearbeitet wird oder RETURNS TABLE
( wobei Parameter implizit verwendet OUT
werden).
round()
mit zwei Parametern funktioniert nur für numeric
Typen. count()
in der Unterabfrage ergibt sich ein bigint
Ergebnis und ein sum()
Over bigint
erzeugt ein numeric
Ergebnis, daher behandeln wir numeric
automatisch eine Zahl und alles passt einfach zusammen.