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

Funkcja pętli nie działa zgodnie z oczekiwaniami

Jest dużo Zrobiłbym inaczej i ze świetnym skutkiem.

Definicja tabeli

Zaczynając od definicji tabeli i konwencji nazewnictwa. Są to w większości tylko opinie:

CREATE TEMP TABLE conta (conta_id bigint primary key, ...);

CREATE TEMP TABLE departamento (
   dept_id   serial PRIMARY KEY
 , master_id int REFERENCES departamento (dept_id)
 , conta_id  bigint NOT NULL REFERENCES conta (conta_id)
 , nome      text NOT NULL
);

Główne punkty

  • Czy na pewno potrzebujesz bigserial dla działów? Niewiele jest na tej planecie. Zwykły serial powinno wystarczyć.

  • Prawie nigdy nie używam character varying z ograniczeniem długości. W przeciwieństwie do niektórych innych systemów zarządzania bazą danych (RDBMS) zastosowanie ograniczenia nie zwiększa wydajności. Dodaj CHECK ograniczenie, jeśli naprawdę potrzebujesz wymusić maksymalną długość. Po prostu używam text , głównie i oszczędź sobie kłopotów.

  • Proponuję konwencję nazewnictwa, w której kolumna klucza obcego dzieli nazwę z kolumną, do której się odwołuje, więc master_id zamiast master_fk , itp. Pozwala również na użycie USING w złączeniach.

  • A ja rzadko użyj nieopisowej nazwy kolumny id . Korzystanie z dept_id zamiast tego tutaj.

Funkcja PL/pgSQL

Można to w dużym stopniu uprościć do:

CREATE OR REPLACE FUNCTION f_retornar_plpgsql(lista_ini_depts VARIADIC int[])
  RETURNS int[] AS
$func$
DECLARE
   _row departamento;                     -- %ROWTYPE is just noise
BEGIN

IF NOT EXISTS (                           -- simpler in 9.1+, see below
    SELECT FROM pg_catalog.pg_class
    WHERE  relnamespace = pg_my_temp_schema()
    AND    relname      = 'tbl_temp_dptos') THEN

   CREATE TEMP TABLE tbl_temp_dptos (dept_id bigint NOT NULL)
   ON COMMIT DELETE ROWS;
END IF;

FOR i IN array_lower(lista_ini_depts, 1)  -- simpler in 9.1+, see below
      .. array_upper(lista_ini_depts, 1) LOOP
   SELECT *  INTO _row                    -- since rowtype is defined, * is best
   FROM   departamento
   WHERE  dept_id = lista_ini_depts[i];

   CONTINUE WHEN NOT FOUND;

   INSERT INTO tbl_temp_dptos VALUES (_row.dept_id);

   LOOP
      SELECT *  INTO _row
      FROM   departamento
      WHERE  dept_id = _row.master_id;

      EXIT WHEN NOT FOUND;

      INSERT INTO tbl_temp_dptos
      SELECT _row.dept_id
      WHERE  NOT EXISTS (
         SELECT FROM tbl_temp_dptos
         WHERE dept_id =_row.dept_id);
   END LOOP;
END LOOP;

RETURN ARRAY(SELECT dept_id FROM tbl_temp_dptos);

END
$func$  LANGUAGE plpgsql;

Zadzwoń:

SELECT f_retornar_plpgsql(2, 5);

Lub:

SELECT f_retornar_plpgsql(VARIADIC '{2,5}');

Wszystko, co zostało powiedziane, nadchodzi wpadka:większości z tego nie potrzebujesz.

Funkcja SQL z rCTE

Nawet w Postgres 9.0 rekurencyjne CTE czyni to o wiele prostszym :

CREATE OR REPLACE FUNCTION f_retornar_sql(lista_ini_depts VARIADIC int[])
  RETURNS int[] AS
$func$
WITH RECURSIVE cte AS (
   SELECT dept_id, master_id
   FROM   unnest($1) AS t(dept_id)
   JOIN   departamento USING (dept_id)

   UNION ALL
   SELECT d.dept_id, d.master_id
   FROM   cte
   JOIN   departamento d ON d.dept_id = cte.master_id
   )
SELECT ARRAY(SELECT DISTINCT dept_id FROM cte)    -- distinct values
$func$  LANGUAGE sql;

To samo połączenie.

Ściśle powiązana odpowiedź z wyjaśnieniem:

SQL Fiddle demonstruje oba.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Heroku pg:push psql:FATAL:uwierzytelnienie hasła nie powiodło się dla użytkownika

  2. Jak używać jpa danych wiosennych do zapytania kolumny jsonb?

  3. Sparametryzowana PostgreSQL funkcja Order By / Limit w tabeli

  4. Jak uprościć zapytanie wybierające, które zawiera wiele wewnętrznych selekcji i zwiększyć wydajność PostgreSQL

  5. Oracle do PostgreSQL — kursory i wspólne wyrażenia tabelowe