sql >> Databasteknik >  >> RDS >> PostgreSQL

PostgreSQL:FEL:42601:en kolumndefinitionslista krävs för funktioner som returnerar post

Återställ valda kolumner

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

Ring:

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

Du hade DECLARE result record; men använde inte variabeln. Jag tog bort cruften.

Du kan returnera posten direkt från UPDATE , vilket är mycket snabbare än att anropa ytterligare SELECT påstående. Använd RETURN QUERY och UPDATE med en RETURNING klausul.

Om användaren inte är _online , standard till en vanlig SELECT . Detta är också den (säkra) standarden om den andra parametern utelämnas - vilket endast är möjligt efter att den förinställda standarden har angetts med DEFAULT false i funktionsdefinitionen.

Om du inte tabellkvalificerar kolumnnamn (tablename.columnname ) i frågor i funktionen, var försiktig med namnkonflikter mellan kolumnnamn och namngivna parametrar, som är synliga (de flesta) överallt i en funktion.
Du kan också undvika sådana konflikter genom att använda positionsreferenser ($n ) för parametrar. Eller använd ett prefix som du aldrig använd för kolumnnamn:som ett understreck (_username ).

Om users.username är definierad som unik i din tabell, sedan LIMIT 1 i den andra frågan är bara cruft. Om det är inte , sedan UPDATE kan uppdatera flera rader, vilket med största sannolikhet är fel . Jag antar ett unikt username och trimma ljudet.

Definiera returtypen för funktionen (som @ertx demonstrerat) eller så måste du tillhandahålla en kolumndefinitionslista med varje funktionsanrop, vilket är besvärligt.

Att skapa en typ för det ändamålet (som @ertx föreslagit) är ett giltigt tillvägagångssätt, men förmodligen överdrivet för en enskild funktion. Det var vägen att gå i gamla versioner av Postgres innan vi hade RETURNS TABLE för det ändamålet - som visats ovan.

Du behöver ingen loop för denna enkla funktion.

Varje funktion behöver en språkdeklaration. LANGUAGE plpgsql i det här fallet.

Jag använder timestamptz (timestamp with time zone ) istället för timestamp (timestamp without time zone ), vilket är den normala standarden. Se:

  • Ignorerar tidszoner helt och hållet i Rails och PostgreSQL

Returnera (uppsättning av) hela rad(er)

För att returnera alla kolumner av den befintliga tabellen users , det finns ett enklare sätt. Postgres definierar automatiskt en sammansatt typ med samma namn för varje tabell . Använd bara RETURNS SETOF users för att avsevärt förenkla frågan:

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

Returnera hela raden plus anpassat tillägg

För att ta itu med frågan som lagts till av TheRealChx101 i en kommentar nedan:

Tänk om du också har ett beräknat värde utöver en hel tabell? 😑

Inte lika enkelt, men genomförbart. Vi kan skicka hela radtypen som en och lägg till fler:

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

"Magin" finns i funktionsanropet, där vi (valfritt) bryter ner radtypen:

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

db<>spela här (visar alla)

Om du behöver något mer "dynamiskt", överväg:

  • Refaktorera en PL/pgSQL-funktion för att returnera utdata från olika SELECT-frågor


  1. SQL Server ORDER BY datum och nollvärden sist

  2. T-SQL XOR-operatör

  3. Hur kopierar jag SQL Azure-databas till min lokala utvecklingsserver?

  4. Kan MySQL ersätta flera tecken?