Det finns mycket Jag skulle göra annorlunda, och med stor effekt.
Tabelldefinition
Börjar med tabelldefinitionen och namnkonventioner. Dessa är för det mesta bara åsikter:
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
);
Huvudpunkter
-
Är du säker på att du behöver en
bigserial
för avdelningar? Det finns knappast så många på denna planet. En vanligserial
borde räcka. -
Jag använder nästan aldrig
character varying
med längdbegränsning. Till skillnad från vissa andra RDBMS finns det ingen som helst prestandavinst genom att använda en begränsning. Lägg till enCHECK
begränsning om du verkligen behöver tillämpa en maximal längd. Jag använder baratext
, mestadels och spara mig besväret. -
Jag föreslår en namnkonvention där kolumnen för främmande nyckel delar namnet med den refererade kolumnen, så
master_id
istället förmaster_fk
, etc. Gör det också möjligt att användaUSING
i går. -
Och jag sällan använd det icke-beskrivande kolumnnamnet
id
. Använderdept_id
istället här.
PL/pgSQL-funktion
Det kan till stor del förenklas till:
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;
Ring:
SELECT f_retornar_plpgsql(2, 5);
Eller:
SELECT f_retornar_plpgsql(VARIADIC '{2,5}');
-
ALIAS FOR $1
är föråldrad syntax och avskräckt . Använd funktionsparametrar istället. -
VARIADIC
parameter gör det bekvämare att ringa. Relaterat: -
Du behöver inte
EXECUTE
för frågor utan dynamiska element. Inget att vinna här. -
Du behöver ingen undantagshantering för att skapa en tabell. Citerar manualen här :
-
Postgres 9.1 eller senare har
CREATE TEMP TABLE IF NOT EXISTS
. Jag använder en lösning för 9.0 för att villkorligt skapa temptabellen. -
Postgres 9.1 erbjuder även
FOREACH
att gå igenom en array .
Allt som sagt, här kommer besvikelsen:du behöver inte det mesta av det här.
SQL-funktion med rCTE
Även i Postgres 9.0, en rekursiv CTE gör det här mycket enklare :
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;
Samma samtal.
Närbesläktat svar med förklaring:
SQL-fiol som demonstrerar båda.