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łyserial
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. DodajCHECK
ograniczenie, jeśli naprawdę potrzebujesz wymusić maksymalną długość. Po prostu używamtext
, 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
zamiastmaster_fk
, itp. Pozwala również na użycieUSING
w złączeniach. -
A ja rzadko użyj nieopisowej nazwy kolumny
id
. Korzystanie zdept_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}');
-
ALIAS FOR $1
jest przestarzała składnia i odradza się . Zamiast tego użyj parametrów funkcji. -
VARIADIC
parametr sprawia, że dzwonienie jest wygodniejsze. Powiązane: -
Nie potrzebujesz
EXECUTE
dla zapytań bez elementów dynamicznych. Nie ma tu nic do zyskania. -
Nie potrzebujesz obsługi wyjątków, aby utworzyć tabelę. Cytując instrukcję tutaj :
-
Postgres 9.1 lub nowszy ma
CREATE TEMP TABLE IF NOT EXISTS
. Używam obejścia dla wersji 9.0, aby warunkowo utworzyć tabelę tymczasową. -
Postgres 9.1 oferuje również
FOREACH
przechodzenie przez tablice .
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: