sql >> Databasteknik >  >> RDS >> PostgreSQL

Hur returnerar man resultatet av en SELECT inuti en funktion i PostgreSQL?

Använd 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$;

Ring:

SELECT * FROM word_frequency(123);

Att uttryckligen definiera returtypen är mycket mer praktiskt än att returnera en generisk record . På så sätt behöver du inte tillhandahålla en kolumndefinitionslista med varje funktionsanrop. RETURNS TABLE är ett sätt att göra det. Det finns andra. Datatyper för OUT parametrar måste matcha exakt vad som returneras av frågan.

Välj namn för OUT parametrar noggrant. De är synliga i funktionskroppen nästan var som helst. Tabellkvalificera kolumner med samma namn för att undvika konflikter eller oväntade resultat. Jag gjorde det för alla kolumner i mitt exempel.

Men notera den potentiella namnkonflikten mellan OUT parameter cnt och kolumnaliaset med samma namn. I det här specifika fallet (RETURN QUERY SELECT ... ) Postgres använder kolumnaliaset över OUT parameter åt båda hållen. Detta kan dock vara tvetydigt i andra sammanhang. Det finns olika sätt att undvika förvirring:

  1. Använd objektets ordningsposition i SELECT-listan:ORDER BY 2 DESC . Exempel:
    • Välj första raden i varje GROUP BY-grupp?
  2. Upprepa uttrycket ORDER BY count(*) .
  3. (Inte tillämpligt här.) Ställ in konfigurationsparametern plpgsql.variable_conflict eller använd specialkommandot #variable_conflict error | use_variable | use_column i funktionen. Se:
    • Namnkonflikt mellan funktionsparameter och resultat av JOIN med USING-sats

Använd inte "text" eller "count" som kolumnnamn. Båda är lagliga att använda i Postgres, men "räkna" är ett reserverat ord i standard SQL och ett grundläggande funktionsnamn och "text" är en grundläggande datatyp. Kan leda till förvirrande fel. Jag använder txt och cnt i mina exempel kanske du vill ha mer explicita namn.

Lade till en saknad ; och korrigerade ett syntaxfel i rubriken. (_max_tokens int) , inte (int maxTokens) - typ efter namn .

När du arbetar med heltalsdivision är det bättre att multiplicera först och dividera senare för att minimera avrundningsfelet. Eller arbeta med numeric eller en flyttalstyp. Se nedan.

Alternativ

Detta är vad jag tror din fråga borde faktiskt se ut (beräknar en relativ andel per 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$;

Uttrycket sum(t.cnt) OVER () är en fönsterfunktion. Du kunde använd en CTE istället för underfrågan. Snyggt, men en underfråga är vanligtvis billigare i enkla fall som det här (mest före Postgres 12).

En sista uttrycklig RETURN uttalandet är inte krävs (men tillåtet) när du arbetar med OUT parametrar eller RETURNS TABLE (som gör implicit användning av OUT parametrar).

round() med två parametrar fungerar bara för numeric typer. count() i underfrågan producerar en bigint resultat och en sum() över denna bigint producerar en numeric resultat, därför hanterar vi en numeric nummer automatiskt och allt faller bara på plats.



  1. Hur fixar man Ora-01427 enkelrads underfråga returnerar mer än en rad i urval?

  2. JSON_MERGE_PATCH() – Utför en RFC 7396-kompatibel sammanslagning av JSON-dokument i MySQL

  3. Prestandatestmetoder:Upptäcka ett nytt sätt

  4. Låt inte Streams Pool lura dig