Usa 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$;
Chiama:
SELECT * FROM word_frequency(123);
La definizione esplicita del tipo restituito è molto più pratico della restituzione di un record
generico . In questo modo non è necessario fornire un elenco di definizioni di colonna con ogni chiamata di funzione. RETURNS TABLE
è un modo per farlo. Ce ne sono altri. Tipi di dati di OUT
i parametri devono corrispondere esattamente a ciò che viene restituito dalla query.
Scegli i nomi per OUT
parametri con attenzione. Sono visibili nel corpo della funzione quasi ovunque. Qualifica le colonne con lo stesso nome per evitare conflitti o risultati imprevisti. L'ho fatto per tutte le colonne nel mio esempio.
Ma nota il potenziale conflitto di denominazione tra il OUT
parametro cnt
e l'alias di colonna con lo stesso nome. In questo caso particolare (RETURN QUERY SELECT ...
) Postgres usa l'alias di colonna sopra OUT
parametro in entrambi i casi. Questo può essere ambiguo in altri contesti, però. Esistono vari modi per evitare qualsiasi confusione:
- Utilizzare la posizione ordinale dell'elemento nell'elenco SELECT:
ORDER BY 2 DESC
. Esempio:- Seleziona la prima riga in ogni gruppo GROUP BY?
- Ripetere l'espressione
ORDER BY count(*)
. - (Non applicabile qui.) Imposta il parametro di configurazione
plpgsql.variable_conflict
oppure usa il comando speciale#variable_conflict error | use_variable | use_column
nella funzione. Vedi:- Conflitto di denominazione tra parametro di funzione e risultato di JOIN con clausola USING
Non utilizzare "testo" o "conta" come nomi di colonna. Entrambi sono legali da usare in Postgres, ma "count" è una parola riservata in SQL standard e un nome di funzione di base e "testo" è un tipo di dati di base. Può portare a errori di confusione. Uso txt
e cnt
nei miei esempi, potresti volere nomi più espliciti.
Aggiunto un ;
mancante e corretto un errore di sintassi nell'intestazione. (_max_tokens int)
, non (int maxTokens)
- digitare dopo nome .
Mentre si lavora con la divisione di interi, è meglio moltiplicare prima e dividere in seguito, per ridurre al minimo l'errore di arrotondamento. Oppure lavora con numeric
o un tipo a virgola mobile. Vedi sotto.
Alternativa
Questo è ciò che penso la tua query dovrebbe effettivamente assomigliare a (calcolo di una quota relativa 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$;
L'espressione sum(t.cnt) OVER ()
è una funzione della finestra. Potresti utilizzare un CTE invece della sottoquery. Carina, ma una sottoquery è in genere più economica in casi semplici come questo (principalmente prima di Postgres 12).
Un esplicito RETURN
finale dichiarazione è non richiesto (ma consentito) quando si lavora con OUT
parametri o RETURNS TABLE
(che fa uso implicito di OUT
parametri).
round()
con due parametri funziona solo per numeric
tipi. count()
nella sottoquery produce un bigint
risultato e un sum()
su questo bigint
produce un numeric
risultato, quindi abbiamo a che fare con un numeric
numero automaticamente e tutto va a posto.