it-swarm.com.de

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

Ich habe diese Funktion in PostgreSQL, weiß aber nicht, wie ich das Ergebnis der Abfrage zurückgeben soll:

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;

Ich weiß aber nicht, wie ich das Ergebnis der Abfrage in der PostgreSQL-Funktion zurückgeben soll.

Ich fand, dass der Rückgabetyp SETOF RECORD Sein sollte, richtig? Aber der Rücksprungbefehl ist nicht richtig.

Was ist der richtige Weg, um dies zu tun?

83
Renato Dinhani

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

Anruf:

SELECT * FROM Word_frequency(123);

Erläuterung:

  • Es ist much praktischer, den Rückgabetyp explizit zu definieren, als ihn einfach als Datensatz zu deklarieren. Auf diese Weise müssen Sie nicht bei jedem Funktionsaufruf eine Spaltendefinitionsliste angeben. RETURNS TABLE ist eine Möglichkeit, dies zu tun. Da sind andere. Datentypen von OUT -Parametern müssen genau mit dem übereinstimmen, was von der Abfrage zurückgegeben wird.

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

    Beachten Sie jedoch das potenzielle Namenskonflikt ​​zwischen dem OUT -Parameter cnt und dem gleichnamigen Spaltenalias. In diesem speziellen Fall (RETURN QUERY SELECT ...) Verwendet Postgres den Spaltenalias über dem Parameter OUT. Dies kann jedoch in anderen Zusammenhängen mehrdeutig sein. Es gibt verschiedene Möglichkeiten, Verwirrung zu vermeiden:

    1. Verwenden Sie die Ordnungszahl des Elements in der SELECT-Liste: ORDER BY 2 DESC. Beispiel:
    2. Wiederholen Sie den Ausdruck ORDER BY count(*).
    3. (Gilt hier nicht.) Stellen Sie den Konfigurationsparameter plpgsql.variable_conflict Ein oder verwenden Sie den Sonderbefehl #variable_conflict error | use_variable | use_column In der Funktion. Sehen:
  • Verwenden Sie nicht "text" oder "count" als Spaltennamen. Beide sind in Postgres zulässig, aber "count" ist ein reserviertes Wort ​​ in Standard-SQL und ein grundlegender Funktionsname und "text" ein grundlegender Datentyp. Kann zu verwirrenden Fehlern führen. In meinen Beispielen verwende ich txt und cnt.

  • Ein fehlender ; Wurde hinzugefügt und ein Syntaxfehler im Header behoben. (_max_tokens int), Nicht (int maxTokens) - Typ nach Name.

  • Beim Arbeiten mit der Ganzzahldivision ist es besser, zuerst zu multiplizieren und später zu dividieren, um den Rundungsfehler zu minimieren. Noch besser: Arbeiten Sie mit numeric (oder einem Gleitkommatyp). Siehe unten.

Alternative

Dies ist, was ich denke Ihre Abfrage sollte tatsächlich 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) 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;

Der Ausdruck sum(t.cnt) OVER () ist eine Fensterfunktion . Sie könnten verwenden ein [~ # ~] cte [~ # ~] anstelle der Unterabfrage - hübsch, aber eine Unterabfrage ist in der Regel billiger einfache Fälle wie dieser.

Eine endgültige explizite RETURN -Anweisung ist nicht erforderlich (aber zulässig), wenn mit OUT -Parametern gearbeitet wird, oder RETURNS TABLE (Wobei implizit OUT -Parameter verwendet werden).

round() mit zwei Parametern funktioniert nur für numeric Typen. count() in der Unterabfrage erzeugt ein bigint Ergebnis und eine sum() über diesem bigint erzeugt ein numeric Ergebnis, womit wir uns befassen Eine numeric Nummer automatisch und alles passt einfach zusammen.

108

Hallo, bitte überprüfen Sie den unten stehenden Link

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