Utilizar 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;
Llamada:
SELECT * FROM word_frequency(123);
Explicación:
Es mucho más práctico definir explícitamente el tipo de retorno que simplemente declararlo como registro. De esta manera, no es necesario que proporcione una lista de definiciones de columna con cada llamada a función. RETURNS TABLE
es una forma de hacerlo. Hay otros. Los tipos de datos de los OUT
parámetros deben coincidir exactamente con lo que devuelve la consulta.
Elija los nombres de los OUT
parámetros con cuidado. Son visibles en el cuerpo funcional casi en cualquier lugar. Columnas de calificación de tabla del mismo nombre para evitar conflictos o resultados inesperados. Hice eso para todas las columnas de mi ejemplo.
Pero tenga en cuenta el posible conflicto de nombres entre el OUT
parámetro cnt
y el alias de la columna del mismo nombre. En este caso particular ( RETURN QUERY SELECT ...
) Postgres usa el alias de la columna sobre el OUT
parámetro de cualquier manera. Sin embargo, esto puede ser ambiguo en otros contextos. Hay varias formas de evitar confusiones:
- Use la posición ordinal del elemento de la lista SELECT:
ORDER BY 2 DESC
. Ejemplo:
- Repite la expresión
ORDER BY count(*)
.
- (No se aplica 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:
No utilice "texto" o "contar" 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ásica y "texto" es un tipo de datos básico. Puede dar lugar a errores confusos. Yo uso txt
y cnt
en mis ejemplos.
Se agregó un ;
error de sintaxis faltante y se corrigió en el encabezado. (_max_tokens int)
, no (int maxTokens)
: escriba después del nombre .
Mientras trabaja con la división de enteros, es mejor multiplicar primero y dividir después, para minimizar el error de redondeo. Aún mejor: trabaje con numeric
(o un tipo de punto flotante). Vea abajo.
Alternativa
Esto es lo que yo creo que la consulta en realidad debe ser similar (el cálculo de una participación relativa por ficha ):
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;
La expresión sum(t.cnt) OVER ()
es una función de ventana . Usted podría utilizar un CTE en lugar de la subconsulta - bonito, pero una sub consulta suele ser más barato en casos sencillos como éste.
No se requiere una RETURN
declaración explícita final (pero se permite) 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()
over this bigint
produce un numeric
resultado, por lo que tratamos con un numeric
número automáticamente y todo encaja en su lugar.