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_conflictoppure usa il comando speciale#variable_conflict error | use_variable | use_columnnella 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.