sql >> Base de Datos >  >> RDS >> PostgreSQL

¿Cómo devolver el resultado de un SELECCIONAR dentro de una función en PostgreSQL?

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:

  1. 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?
  2. Repita la expresión ORDER BY count(*) .
  3. (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.