sql >> Databáze >  >> RDS >> PostgreSQL

PostgreSQL:ERROR:42601:pro funkce vracející záznam je vyžadován seznam definic sloupců

Vrátit vybrané sloupce

CREATE OR REPLACE FUNCTION get_user_by_username(_username text
                                              , _online bool DEFAULT false)
  RETURNS TABLE (
    user_id int
  , user_name varchar
  , last_activity timestamptz
  )
  LANGUAGE plpgsql AS
$func$
BEGIN
   IF _online THEN
      RETURN QUERY
      UPDATE users u 
      SET    last_activity = current_timestamp  -- ts with time zone
      WHERE  u.user_name = _username
      RETURNING u.user_id
              , u.user_name
              , u.last_activity;
   ELSE
      RETURN QUERY
      SELECT u.user_id
           , u.user_name
           , u.last_activity
      FROM   users u
      WHERE  u.user_name = _username;
   END IF;
END
$func$;

Volejte:

SELECT * FROM get_user_by_username('myuser', true);

Měli jste DECLARE result record; ale nepoužil proměnnou. Smazal jsem cruft.

Záznam můžete vrátit přímo z UPDATE , což je mnohem rychlejší než volání dalšího SELECT prohlášení. Použijte RETURN QUERY a UPDATE s RETURNING doložka.

Pokud uživatel není _online , výchozí je prostý SELECT . Toto je také (bezpečné) výchozí nastavení, pokud je vynechán druhý parametr – což je možné pouze po zadání tohoto výchozího nastavení s DEFAULT false v definici funkce.

Pokud názvy sloupců nekvalifikujete podle tabulky (tablename.columnname ) v dotazech uvnitř funkce si dávejte pozor na konflikty pojmenování mezi názvy sloupců a pojmenovanými parametry, které jsou viditelné (většinou) všude uvnitř funkce.
Takovým konfliktům se můžete vyhnout také použitím pozičních odkazů ($n ) pro parametry. Nebo použijte předponu, kterou nikdy nepoužíváte použijte pro názvy sloupců:jako podtržítko (_username ).

Pokud users.username je definováno jako jedinečné v tabulce a poté LIMIT 1 ve druhém dotazu je jen cruft. Pokud není a poté UPDATE může aktualizovat více řádků, což je s největší pravděpodobností špatně . Předpokládám jedinečné username a ořízněte hluk.

Definujte typ návratu funkce (jako předvedený @ertx) nebo musíte při každém volání funkce poskytnout seznam definic sloupců, což je nepříjemné.

Vytvoření typu pro tento účel (jako @ertx navrhovaný) je platný přístup, ale pravděpodobně přehnaný pro jednu funkci. To byl způsob, jak jít ve starých verzích Postgres, než jsme měli RETURNS TABLE za tímto účelem – jak je ukázáno výše.

Nepotřebujete smyčku pro tuto jednoduchou funkci.

Každá funkce potřebuje deklaraci jazyka. LANGUAGE plpgsql v tomto případě.

Používám timestamptz (timestamp with time zone ) namísto timestamp (timestamp without time zone ), což je rozumné výchozí nastavení. Viz:

  • Úplné ignorování časových pásem v Rails a PostgreSQL

Vrátí (množinu) celých řádků

Chcete-li vrátit všechny sloupce existující tabulky users , existuje jednodušší způsob. Postgres automaticky definuje složený typ se stejným názvem pro každou tabulku . Stačí použít RETURNS SETOF users k výraznému zjednodušení dotazu:

CREATE OR REPLACE FUNCTION get_user_by_username(_username text
                                              , _online bool DEFAULT false)
  RETURNS SETOF users
  LANGUAGE plpgsql AS
$func$
BEGIN
   IF _online THEN
      RETURN QUERY
      UPDATE users u 
      SET    last_activity = current_timestamp
      WHERE  u.user_name = _username
      RETURNING u.*;
   ELSE
      RETURN QUERY
      SELECT *
      FROM   users u
      WHERE  u.user_name = _username;
   END IF;
END
$func$;

Vrátit celý řádek plus vlastní přidání

Chcete-li odpovědět na otázku, kterou přidal TheRealChx101 v komentáři níže:

Co když máte kromě celé tabulky také vypočítanou hodnotu? 😑

Ne tak jednoduché, ale proveditelné. Celý typ řádku můžeme odeslat jako jeden pole a přidejte další:

CREATE OR REPLACE FUNCTION get_user_by_username3(_username text
                                               , _online bool DEFAULT false)
  RETURNS TABLE (
    users_row users
  , custom_addition text
  )
  LANGUAGE plpgsql AS
$func$
BEGIN
   IF _online THEN
      RETURN QUERY
      UPDATE users u 
      SET    last_activity = current_timestamp  -- ts with time zone
      WHERE  u.user_name = _username
      RETURNING u  -- whole row
              , u.user_name || u.user_id;
   ELSE
      RETURN QUERY
      SELECT u, u.user_name || u.user_id
      FROM   users u
      WHERE  u.user_name = _username;
   END IF;
END
$func$;

"Kouzlo" je ve volání funkce, kde (volitelně) rozložíme typ řádku:

SELECT (users_row).*, custom_addition FROM get_user_by_username('foo', true);

db<>zde hrajte (zobrazuje se vše)

Pokud potřebujete něco „dynamičtějšího“, zvažte:

  • Refaktorujte funkci PL/pgSQL tak, aby vrátila výstup různých SELECT dotazů


  1. Jak vypočítat měsíční růst v PostgreSQL

  2. Vrátit všechny řádky z konkrétního oddílu v SQL Server (T-SQL)

  3. Výsledky MySQL s kódováním JSON

  4. Jakou velikost používáte pro varchar (MAX) v deklaraci parametru?