PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

PostgreSQL:BŁĄD:42601:lista definicji kolumn jest wymagana dla funkcji zwracających rekord

Zwróć wybrane kolumny

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

Zadzwoń:

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

Masz DECLARE result record; ale nie użył zmiennej. Usunąłem cruft.

Możesz zwrócić rekord bezpośrednio z UPDATE , co jest znacznie szybsze niż wywołanie dodatkowego SELECT oświadczenie. Użyj RETURN QUERY i UPDATE z RETURNING klauzula.

Jeśli użytkownik nie jest _online , domyślnie zwykły SELECT . Jest to również (bezpieczna) wartość domyślna, jeśli pominięto drugi parametr - co jest możliwe tylko po podaniu tej wartości domyślnej z DEFAULT false w definicji funkcji.

Jeśli nie kwalifikujesz nazw kolumn w tabeli (tablename.columnname ) w zapytaniach wewnątrz funkcji uważaj na konflikty nazw między nazwami kolumn a nazwanymi parametrami, które są widoczne (w większości) wszędzie wewnątrz funkcji.
Można również uniknąć takich konfliktów, używając odwołań pozycyjnych ($n ) dla parametrów. Lub użyj prefiksu, którego nigdy użyj dla nazw kolumn:jak podkreślenie (_username ).

Jeśli users.username jest zdefiniowany jako unikalny w tabeli, a następnie LIMIT 1 w drugim zapytaniu to po prostu cruft. Jeśli to nie , a następnie UPDATE może zaktualizować wiele wierszy, co najprawdopodobniej jest błędem . Zakładam unikalną username i przytnij hałas.

Zdefiniuj typ zwrotu funkcji (jak zademonstrowano @ertx) lub musisz dostarczyć listę definicji kolumn przy każdym wywołaniu funkcji, co jest niewygodne.

Tworzenie typu do tego celu (jak zaproponowany @ertx) jest prawidłowym podejściem, ale prawdopodobnie przesadą dla pojedynczej funkcji. To była droga w starych wersjach Postgresa, zanim pojawiła się RETURNS TABLE w tym celu - jak pokazano powyżej.

Nie potrzebujesz pętli dla tej prostej funkcji.

Każda funkcja wymaga deklaracji języka. LANGUAGE plpgsql w tym przypadku.

Używam timestamptz (timestamp with time zone ) zamiast timestamp (timestamp without time zone ), co jest rozsądną wartością domyślną. Zobacz:

  • Całkowite ignorowanie stref czasowych w Rails i PostgreSQL

Zwróć (zestaw) całych wierszy

Aby zwrócić wszystkie kolumny istniejącej tabeli users , jest prostszy sposób. Postgres automatycznie definiuje typ złożony o tej samej nazwie dla każdej tabeli . Wystarczy użyć RETURNS SETOF users aby znacznie uprościć zapytanie:

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

Zwróć cały wiersz plus niestandardowy dodatek

Aby odpowiedzieć na pytanie dodane przez TheRealChx101 w poniższym komentarzu:

Co zrobić, jeśli oprócz całej tabeli masz również obliczoną wartość?

Nie tak proste, ale wykonalne. Możemy wysłać cały typ wiersza jako jeden pole i dodaj więcej:

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

„Magia” znajduje się w wywołaniu funkcji, gdzie (opcjonalnie) rozkładamy typ wiersza:

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

db<>graj tutaj (pokazuje wszystko)

Jeśli potrzebujesz czegoś bardziej „dynamicznego”, rozważ:

  • Refaktoryzuj funkcję PL/pgSQL, aby zwrócić dane wyjściowe różnych zapytań SELECT


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Jak wyświetlić indeksy utworzone dla tabeli w postgresie

  2. Napraw „INSERT ma więcej wyrażeń niż kolumn docelowych” w PostgreSQL

  3. HikariCP Postgresql Driver twierdzi, że nie akceptuje adresu URL JDBC

  4. INSERT z dynamiczną nazwą tabeli w funkcji wyzwalacza

  5. Klauzula CHECK dla aktualizowalnych widoków