PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Wie kann ich das Ergebnis eines SELECT innerhalb einer Funktion in PostgreSQL zurückgeben?

Verwenden Sie 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$;

Aufruf:

SELECT * FROM word_frequency(123);

Das explizite Definieren des Rückgabetyps ist viel praktischer als die Rückgabe eines generischen record . Auf diese Weise müssen Sie nicht bei jedem Funktionsaufruf eine Spaltendefinitionsliste bereitstellen. RETURNS TABLE ist eine Möglichkeit, dies zu tun. Da sind andere. Datentypen von OUT Die Parameter müssen genau mit dem übereinstimmen, was von der Abfrage zurückgegeben wird.

Wählen Sie Namen für OUT Parameter sorgfältig. Sie sind im Funktionskörper fast überall sichtbar. Tabellenqualifizieren Sie Spalten mit demselben Namen, um Konflikte oder unerwartete Ergebnisse zu vermeiden. Ich habe das für alle Spalten in meinem Beispiel gemacht.

Beachten Sie jedoch den potenziellen Namenskonflikt zwischen OUT Parameter cnt und der gleichnamige Spaltenalias. In diesem speziellen Fall (RETURN QUERY SELECT ... ) Postgres verwendet den Spaltenalias über OUT Parameter so oder so. Dies kann jedoch in anderen Zusammenhängen mehrdeutig sein. Es gibt verschiedene Möglichkeiten, Verwirrung zu vermeiden:

  1. Verwenden Sie die Ordnungsposition des Elements in der SELECT-Liste:ORDER BY 2 DESC . Beispiel:
    • Erste Zeile in jeder GROUP BY-Gruppe auswählen?
  2. Wiederholen Sie den Ausdruck ORDER BY count(*) .
  3. (Hier nicht zutreffend.) Setzen Sie den Konfigurationsparameter plpgsql.variable_conflict oder verwenden Sie den speziellen Befehl #variable_conflict error | use_variable | use_column in der Funktion. Siehe:
    • Namenskonflikt zwischen Funktionsparameter und Ergebnis von JOIN mit USING-Klausel

Verwenden Sie nicht „text“ oder „count“ als Spaltennamen. Beide können in Postgres legal verwendet werden, aber "count" ist ein reserviertes Wort in Standard-SQL und ein grundlegender Funktionsname und "Text" ist ein grundlegender Datentyp. Kann zu verwirrenden Fehlern führen. Ich verwende txt und cnt In meinen Beispielen möchten Sie vielleicht deutlichere Namen.

Fehlender ; hinzugefügt und einen Syntaxfehler im Header korrigiert. (_max_tokens int) , nicht (int maxTokens) - tippen nach Name .

Beim Arbeiten mit ganzzahliger Division ist es besser, zuerst zu multiplizieren und später zu dividieren, um den Rundungsfehler zu minimieren. Oder arbeiten Sie mit numeric oder ein Fließkommatyp. Siehe unten.

Alternative

Das ist, was ich denke Ihre Abfrage sollte tatsächlich so aussehen (Berechnung eines relativen Anteils pro 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$;

Der Ausdruck sum(t.cnt) OVER () ist eine Fensterfunktion. Sie könnten Verwenden Sie anstelle der Unterabfrage einen CTE. Hübsch, aber eine Unterabfrage ist in einfachen Fällen wie diesem normalerweise billiger (meistens vor Postgres 12).

Ein abschließendes explizites RETURN Aussage ist nicht erforderlich (aber erlaubt), wenn mit OUT gearbeitet wird Parameter oder RETURNS TABLE (was OUT implizit verwendet Parameter).

round() mit zwei Parametern funktioniert nur für numeric Typen. count() in der Unterabfrage erzeugt ein bigint result und eine sum() über diesen bigint erzeugt einen numeric Ergebnis, wir haben es also mit einem numeric zu tun Nummer automatisch und alles fügt sich einfach zusammen.