Utilice 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$;
Llamar:
SELECT * FROM word_frequency(123);
Definir el tipo de devolución explícitamente es mucho más práctico que devolver un record
genérico . De esta manera, no tiene que proporcionar una lista de definición de columna con cada llamada de función. RETURNS TABLE
es una manera de hacer eso. Hay otros. Tipos de datos de OUT
los parámetros tienen que coincidir exactamente con lo que devuelve la consulta.
Elija nombres para OUT
parámetros cuidadosamente. Son visibles en el cuerpo de la función en casi cualquier lugar. Califique en tablas las columnas del mismo nombre para evitar conflictos o resultados inesperados. Hice eso para todas las columnas en mi ejemplo.
Pero tenga en cuenta el posible conflicto de nombres entre el OUT
parámetro cnt
y el alias de columna del mismo nombre. En este caso particular (RETURN QUERY SELECT ...
) Postgres usa el alias de columna sobre OUT
parámetro de cualquier manera. Sin embargo, esto puede ser ambiguo en otros contextos. Hay varias formas de evitar confusiones:
- Utilice la posición ordinal del elemento en la lista SELECCIONAR:
ORDER BY 2 DESC
. Ejemplo:- ¿Seleccionar la primera fila en cada grupo GROUP BY?
- Repita la expresión
ORDER BY count(*)
. - (No aplicable aquí). Establezca el parámetro de configuración
plpgsql.variable_conflict
o utilice el comando especial#variable_conflict error | use_variable | use_column
en la función. Ver:- Conflicto de nombre entre el parámetro de la función y el resultado de JOIN con la cláusula USING
No use "texto" o "recuento" como nombres de columna. Ambos son legales para usar en Postgres, pero "contar" es una palabra reservada en SQL estándar y un nombre de función básico y "texto" es un tipo de datos básico. Puede dar lugar a errores confusos. Yo uso txt
y cnt
en mis ejemplos, es posible que desee nombres más explícitos.
Se agregó un ;
faltante y corrigió un error de sintaxis en el encabezado. (_max_tokens int)
, no (int maxTokens)
- tipo después de nombre .
Mientras trabaja con la división de enteros, es mejor multiplicar primero y dividir después, para minimizar el error de redondeo. O trabajar con numeric
o un tipo de coma flotante. Ver más abajo.
Alternativa
Esto es lo que pienso su consulta debería verse como (calculando una participación relativa por 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$;
La expresión sum(t.cnt) OVER ()
es una función de ventana. podrías use un CTE en lugar de la subconsulta. Bonito, pero una subconsulta suele ser más barata en casos simples como este (principalmente antes de Postgres 12).
Un último RETURN
explícito declaración es no requerido (pero permitido) cuando se trabaja con OUT
parámetros o RETURNS TABLE
(que hace un uso implícito de OUT
parámetros).
round()
con dos parámetros solo funciona para numeric
tipos count()
en la subconsulta produce un bigint
resultado y un sum()
sobre este bigint
produce un numeric
resultado, por lo que tratamos con un numeric
número automáticamente y todo encaja en su lugar.