sql >> Database >  >> RDS >> PostgreSQL

Hoe het resultaat van een SELECT binnen een functie in PostgreSQL te retourneren?

Gebruik 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$;

Bel:

SELECT * FROM word_frequency(123);

Het expliciet definiëren van het retourtype is veel praktischer dan het retourneren van een generiek record . Op deze manier hoeft u niet bij elke functieaanroep een kolomdefinitielijst mee te geven. RETURNS TABLE is een manier om dat te doen. Er zijn anderen. Gegevenstypen van OUT parameters moeten exact overeenkomen met wat wordt geretourneerd door de query.

Kies namen voor OUT parameters zorgvuldig. Ze zijn bijna overal zichtbaar in het functielichaam. Kwalificeer kolommen met dezelfde naam voor tabellen om conflicten of onverwachte resultaten te voorkomen. Ik deed dat voor alle kolommen in mijn voorbeeld.

Maar let op het potentiële naamgevingsconflict tussen de OUT parameter cnt en de kolomalias met dezelfde naam. In dit specifieke geval (RETURN QUERY SELECT ... ) Postgres gebruikt de kolomalias over de OUT parameter hoe dan ook. Dit kan echter dubbelzinnig zijn in andere contexten. Er zijn verschillende manieren om verwarring te voorkomen:

  1. Gebruik de ordinale positie van het item in de SELECT-lijst:ORDER BY 2 DESC . Voorbeeld:
    • Selecteer de eerste rij in elke GROUP BY-groep?
  2. Herhaal de uitdrukking ORDER BY count(*) .
  3. (Hier niet van toepassing.) Stel de configuratieparameter plpgsql.variable_conflict in of gebruik het speciale commando #variable_conflict error | use_variable | use_column in de functie. Zie:
    • Naamconflict tussen functieparameter en resultaat van JOIN met de clausule USING

Gebruik "tekst" of "tel" niet als kolomnamen. Beide zijn legaal om te gebruiken in Postgres, maar 'count' is een gereserveerd woord in standaard SQL en een basisfunctienaam en "tekst" is een basisgegevenstype. Kan leiden tot verwarrende fouten. Ik gebruik txt en cnt in mijn voorbeelden wil je misschien meer expliciete namen.

Een ontbrekende ; . toegevoegd en een syntaxisfout in de koptekst gecorrigeerd. (_max_tokens int) , niet (int maxTokens) - type na naam .

Bij het werken met integer delen, is het beter om eerst te vermenigvuldigen en later te delen, om de afrondingsfout te minimaliseren. Of werk met numeric of een zwevend-kommatype. Zie hieronder.

Alternatief

Dit is wat ik denk uw zoekopdracht zou er eigenlijk zo uit moeten zien (berekening van een relatief aandeel 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$;

De uitdrukking sum(t.cnt) OVER () is een vensterfunctie. Je zou gebruik een CTE in plaats van de subquery. Mooi, maar een subquery is doorgaans goedkoper in eenvoudige gevallen zoals deze (meestal vóór Postgres 12).

Een laatste expliciete RETURN verklaring is niet vereist (maar toegestaan) bij het werken met OUT parameters of RETURNS TABLE (die impliciet gebruik maakt van OUT parameters).

round() met twee parameters werkt alleen voor numeric soorten. count() in de subquery produceert een bigint resultaat en een sum() over deze bigint produceert een numeric resultaat, dus we hebben te maken met een numeric nummer automatisch en alles valt gewoon op zijn plaats.



  1. SQL:alleen de eerste letter met een hoofdletter

  2. FOUT:kon stat-bestand niet XX.csv:onbekende fout

  3. Hoe de Modulo-operator werkt in MariaDB

  4. Vastgelopen zoekopdracht beëindigen (inactief in transactie)