sql >> Database teknologi >  >> RDS >> PostgreSQL

Hvordan returnerer man resultatet af en SELECT i en funktion i PostgreSQL?

Brug 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 til:

SELECT * FROM word_frequency(123);

At definere returtypen eksplicit er meget mere praktisk end at returnere en generisk record . På denne måde behøver du ikke at levere en kolonnedefinitionsliste med hvert funktionskald. RETURNS TABLE er en måde at gøre det på. Der er andre. Datatyper af OUT parametre skal matche nøjagtigt, hvad der returneres af forespørgslen.

Vælg navne til OUT parametre omhyggeligt. De er synlige i funktionskroppen næsten overalt. Tabelkvalificerer kolonner af samme navn for at undgå konflikter eller uventede resultater. Det gjorde jeg for alle kolonner i mit eksempel.

Men bemærk den potentielle navnekonflikt mellem OUT parameter cnt og kolonnealiaset af samme navn. I dette særlige tilfælde (RETURN QUERY SELECT ... ) Postgres bruger kolonnealiaset over OUT parameter begge veje. Dette kan dog være tvetydigt i andre sammenhænge. Der er forskellige måder at undgå forvirring på:

  1. Brug ordenspositionen for elementet i SELECT-listen:ORDER BY 2 DESC . Eksempel:
    • Vælg første række i hver GROUP BY-gruppe?
  2. Gentag udtrykket ORDER BY count(*) .
  3. (Ikke relevant her.) Indstil konfigurationsparameteren plpgsql.variable_conflict eller brug den specielle kommando #variable_conflict error | use_variable | use_column i funktionen. Se:
    • Navngivningskonflikt mellem funktionsparameter og resultat af JOIN med USING-sætning

Brug ikke "tekst" eller "tæller" som kolonnenavne. Begge er lovlige at bruge i Postgres, men "tælle" er et reserveret ord i standard SQL og et grundlæggende funktionsnavn og "tekst" er en grundlæggende datatype. Kan føre til forvirrende fejl. Jeg bruger txt og cnt i mine eksempler vil du måske have mere eksplicitte navne.

Tilføjet en manglende ; og rettede en syntaksfejl i overskriften. (_max_tokens int) , ikke (int maxTokens) - skriv efter navn .

Mens du arbejder med heltalsdivision, er det bedre at gange først og dividere senere for at minimere afrundingsfejlen. Eller arbejd med numeric eller en flydende kommatype. Se nedenfor.

Alternativ

Det er hvad jeg mener din forespørgsel skal faktisk se ud (beregner en relativ andel pr. 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$;

Udtrykket sum(t.cnt) OVER () er en vinduesfunktion. Du kunne brug en CTE i stedet for underforespørgslen. Smukt, men en underforespørgsel er typisk billigere i simple tilfælde som denne (for det meste før Postgres 12).

En sidste eksplicit RETURN erklæring er ikke påkrævet (men tilladt), når du arbejder med OUT parametre eller RETURNS TABLE (som gør implicit brug af OUT parametre).

round() med to parametre virker kun for numeric typer. count() i underforespørgslen producerer en bigint resultat og en sum() over denne bigint producerer en numeric resultat, derfor beskæftiger vi os med en numeric nummer automatisk, og alt falder bare på plads.



  1. Kan jeg bruge en PDO-forberedt sætning til at binde en identifikator (et tabel- eller feltnavn) eller et syntaksnøgleord?

  2. Del forbindelse til postgres db på tværs af processer i Python

  3. En introduktion til MySQL-datatyper

  4. Sammenføjning af forskellige datakilder i lagdeling