Er is veel Ik zou het anders doen, en met groot effect.
Tabeldefinitie
Beginnend met de tabeldefinitie en naamgevingsconventies. Dit zijn meestal slechts meningen:
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
);
Belangrijkste punten
-
Weet je zeker dat je een
bigserial
voor afdelingen? Er zijn er nauwelijks zoveel op deze planeet. Een gewoneserial
zou voldoende moeten zijn. -
Ik gebruik zelden
karakter variërende
met een lengtebeperking. In tegenstelling tot sommige andere RDBMS is er geen enkele prestatiewinst door het gebruik van een beperking. Voeg eenCHECK
toe beperking als u echt een maximale lengte moet afdwingen. Ik gebruik gewoontext
, meestal en bespaar mezelf de moeite. -
Ik stel een naamgevingsconventie voor waarbij de kolom met de refererende sleutel de naam deelt met de kolom waarnaar wordt verwezen, dus
master_id
in plaats vanmaster_fk
, etc. Staat ook toe omUSING
. te gebruiken in samenvoegingen. -
En ik zelden gebruik de niet-beschrijvende kolomnaam
id
.dept_id
gebruiken in plaats daarvan hier.
PL/pgSQL-functie
Het kan grotendeels worden vereenvoudigd tot:
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;
Bel:
SELECT f_retornar_plpgsql(2, 5);
Of:
SELECT f_retornar_plpgsql(VARIADIC '{2,5}');
-
ALIAS VOOR $1
is verouderde syntaxis en ontmoedigd . Gebruik in plaats daarvan functieparameters. -
De
VARIADIC
parameter maakt het gemakkelijker om te bellen. Gerelateerd: -
Je hebt
UITVOEREN
niet nodig voor queries zonder dynamische elementen. Hier valt niets te winnen. -
U hebt geen exception handling nodig om een tabel te maken. Ik citeer de handleiding hier :
-
Postgres 9.1 of hoger heeft
CREATE TEMP TABEL INDIEN NIET BESTAAT
. Ik gebruik een tijdelijke oplossing voor 9.0 om de tijdelijke tabel voorwaardelijk te maken. -
Postgres 9.1 biedt ook
FOREACH
door een array heen lopen .
Dat gezegd hebbende, komt hier het minpunt:je hebt het meeste hiervan niet nodig.
SQL-functie met rCTE
Zelfs in Postgres 9.0, een recursieve CTE maakt dit een stuk eenvoudiger :
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;
Zelfde oproep.
Nauw verwant antwoord met uitleg:
SQL Fiddle die beide demonstreert.