web-dev-qa-db-fra.com

Comment retourner le résultat d'un SELECT dans une fonction dans PostgreSQL?

J'ai cette fonction dans PostgreSQL, mais je ne sais pas comment renvoyer le résultat de la requête:

CREATE OR REPLACE FUNCTION wordFrequency(maxTokens INTEGER)
  RETURNS SETOF RECORD AS
$$
BEGIN
    SELECT text, count(*), 100 / maxTokens * count(*)
    FROM (
        SELECT text
    FROM token
    WHERE chartype = 'ALPHABETIC'
    LIMIT maxTokens
    ) as tokens
    GROUP BY text
    ORDER BY count DESC
END
$$
LANGUAGE plpgsql;

Mais je ne sais pas comment renvoyer le résultat de la requête dans la fonction PostgreSQL.

J'ai trouvé que le type de retour devrait être SETOF RECORD, droite? Mais la commande de retour n'est pas correcte.

Quel est le bon moyen de le faire?

83
Renato Dinhani

Utilisez 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;

Appel:

SELECT * FROM Word_frequency(123);

Explication:

  • Il est beaucoup plus pratique de définir explicitement le type de retour que de simplement le déclarer comme enregistrement. De cette façon, vous n'avez pas à fournir une liste de définition de colonne à chaque appel de fonction. RETURNS TABLE est un moyen de le faire. Il y en a d'autres. Les types de données de OUT paramètres doivent correspondre exactement à ce qui est renvoyé par la requête.

  • Choisissez des noms pour les paramètres OUT avec soin. Ils sont visibles dans le corps de la fonction presque n'importe où. Qualifiez les colonnes du même nom pour éviter les conflits et les résultats inattendus. Je l'ai fait pour toutes les colonnes de mon exemple.

    Mais notez le potentiel conflit de dénomination entre le paramètre OUT, le paramètre cnt et l’alias de colonne du même nom. Dans ce cas particulier (RETURN QUERY SELECT ...), Postgres utilise l’alias de colonne placé au-dessus du paramètre OUT. Cela peut cependant être ambigu dans d'autres contextes. Il y a plusieurs façons d'éviter toute confusion:

    1. Utilisez la position ordinale de l’élément dans la liste SELECT: ORDER BY 2 DESC. Exemple:
    2. Répétez l'expression ORDER BY count(*).
    3. (Non applicable ici.) Définissez le paramètre de configuration plpgsql.variable_conflict Ou utilisez la commande spéciale #variable_conflict error | use_variable | use_column Dans la fonction. Voir:
  • N'utilisez pas "texte" ou "compte" comme noms de colonne. Les deux sont légaux à utiliser dans Postgres, mais "count" est un réservé Word en SQL standard et un nom de fonction de base et "texte" est un type de données de base. Peut conduire à des erreurs déroutantes. J'utilise txt et cnt dans mes exemples.

  • Ajout d'un ; Manquant et correction d'une erreur de syntaxe dans l'en-tête. (_max_tokens int), Pas (int maxTokens) - tapez après name.

  • Lorsque vous travaillez avec une division entière, il est préférable de se multiplier d'abord et de se diviser plus tard, afin de minimiser l'erreur d'arrondi. Encore mieux: travaillez avec numeric (ou un type à virgule flottante). Voir ci-dessous.

Alternative

C’est ce que je pense votre requête devrait ressembler à ceci (calcul = part relative par jeton):

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;

L'expression sum(t.cnt) OVER () est un fonction window . Vous pouvez utilisez un CTE au lieu de la sous-requête - jolie, mais une sous-requête est généralement moins chère dans des cas simples comme celui-ci .

Une dernière instruction instruction RETURN explicite est pas obligatoire (mais autorisée) lorsque vous utilisez OUT paramètres ou RETURNS TABLE (qui utilise implicitement les paramètres OUT).

round() avec deux paramètres ne fonctionne que pour numeric les types. count() dans la sous-requête produit un résultat bigint et une fonction sum() sur celle-ci bigint produit un résultat numeric, nous allons donc traiter un numéro numeric automatiquement et tout se met bien en place.

108
Erwin Brandstetter

Salut s'il vous plaît vérifier le lien ci-dessous

https://www.postgresql.org/docs/current/xfunc-sql.html

EX:

CREATE FUNCTION sum_n_product_with_tab (x int)
RETURNS TABLE(sum int, product int) AS $$
    SELECT $1 + tab.y, $1 * tab.y FROM tab;
$$ LANGUAGE SQL;
0
Moumita Das