Gebruik 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$;
Bel:
SELECT * FROM word_frequency(123);
Het expliciet definiëren van het retourtype is veel praktischer dan het retourneren van een generiek record
. Op deze manier hoeft u niet bij elke functieaanroep een kolomdefinitielijst mee te geven. RETURNS TABLE
is een manier om dat te doen. Er zijn anderen. Gegevenstypen van OUT
parameters moeten exact overeenkomen met wat wordt geretourneerd door de query.
Kies namen voor OUT
parameters zorgvuldig. Ze zijn bijna overal zichtbaar in het functielichaam. Kwalificeer kolommen met dezelfde naam voor tabellen om conflicten of onverwachte resultaten te voorkomen. Ik deed dat voor alle kolommen in mijn voorbeeld.
Maar let op het potentiële naamgevingsconflict tussen de OUT
parameter cnt
en de kolomalias met dezelfde naam. In dit specifieke geval (RETURN QUERY SELECT ...
) Postgres gebruikt de kolomalias over de OUT
parameter hoe dan ook. Dit kan echter dubbelzinnig zijn in andere contexten. Er zijn verschillende manieren om verwarring te voorkomen:
- Gebruik de ordinale positie van het item in de SELECT-lijst:
ORDER BY 2 DESC
. Voorbeeld:- Selecteer de eerste rij in elke GROUP BY-groep?
- Herhaal de uitdrukking
ORDER BY count(*)
. - (Hier niet van toepassing.) Stel de configuratieparameter
plpgsql.variable_conflict
in of gebruik het speciale commando#variable_conflict error | use_variable | use_column
in de functie. Zie:- Naamconflict tussen functieparameter en resultaat van JOIN met de clausule USING
Gebruik "tekst" of "tel" niet als kolomnamen. Beide zijn legaal om te gebruiken in Postgres, maar 'count' is een gereserveerd woord in standaard SQL en een basisfunctienaam en "tekst" is een basisgegevenstype. Kan leiden tot verwarrende fouten. Ik gebruik txt
en cnt
in mijn voorbeelden wil je misschien meer expliciete namen.
Een ontbrekende ;
. toegevoegd en een syntaxisfout in de koptekst gecorrigeerd. (_max_tokens int)
, niet (int maxTokens)
- type na naam .
Bij het werken met integer delen, is het beter om eerst te vermenigvuldigen en later te delen, om de afrondingsfout te minimaliseren. Of werk met numeric
of een zwevend-kommatype. Zie hieronder.
Alternatief
Dit is wat ik denk uw zoekopdracht zou er eigenlijk zo uit moeten zien (berekening van een relatief aandeel per 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$;
De uitdrukking sum(t.cnt) OVER ()
is een vensterfunctie. Je zou gebruik een CTE in plaats van de subquery. Mooi, maar een subquery is doorgaans goedkoper in eenvoudige gevallen zoals deze (meestal vóór Postgres 12).
Een laatste expliciete RETURN
verklaring is niet vereist (maar toegestaan) bij het werken met OUT
parameters of RETURNS TABLE
(die impliciet gebruik maakt van OUT
parameters).
round()
met twee parameters werkt alleen voor numeric
soorten. count()
in de subquery produceert een bigint
resultaat en een sum()
over deze bigint
produceert een numeric
resultaat, dus we hebben te maken met een numeric
nummer automatisch en alles valt gewoon op zijn plaats.