Es gibt viel Ich würde es anders machen und mit großer Wirkung.
Tabellendefinition
Angefangen bei der Tabellendefinition und den Namenskonventionen. Dies sind meistens nur Meinungen:
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
);
Wichtige Punkte
-
Sind Sie sicher, dass Sie eine
bigserial
für Abteilungen? Es gibt kaum so viele auf diesem Planeten. Eine einfacheSeriennummer
sollte reichen. -
Ich verwende kaum
variable Zeichen
mit Längenbeschränkung. Anders als bei manch anderem RDBMS gibt es keinerlei Performance-Gewinn durch die Verwendung einer Restriktion. Fügen Sie einCHECK
hinzu Einschränkung, wenn Sie wirklich eine maximale Länge erzwingen müssen. Ich verwende einfachtext
, meistens und erspare mir die Mühe. -
Ich schlage eine Namenskonvention vor, bei der die Fremdschlüsselspalte den Namen mit der referenzierten Spalte teilt, also
master_id
stattmaster_fk
usw. Erlaubt auch die Verwendung vonUSING
in Joins. -
Und ich selten Verwenden Sie den nicht beschreibenden Spaltennamen
id
. Verwenden vondept_id
stattdessen hier.
PL/pgSQL-Funktion
Es kann weitgehend vereinfacht werden zu:
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;
Aufruf:
SELECT f_retornar_plpgsql(2, 5);
Oder:
SELECT f_retornar_plpgsql(VARIADIC '{2,5}');
-
ALIAS FÜR $1
ist eine veraltete Syntax und wird nicht empfohlen . Verwenden Sie stattdessen Funktionsparameter. -
Die
VARIADIC
Parameter macht es bequemer anzurufen. Verwandte: -
Sie brauchen
EXECUTE
nicht für Abfragen ohne dynamische Elemente. Hier gibt es nichts zu gewinnen. -
Sie benötigen keine Ausnahmebehandlung, um eine Tabelle zu erstellen. Zitieren Sie das Handbuch hier :
-
Postgres 9.1 oder höher hat
CREATE TEMP-TABELLE, WENN NICHT VORHANDEN
. Ich verwende eine Problemumgehung für 9.0, um die temporäre Tabelle bedingt zu erstellen. -
Postgres 9.1 bietet auch
FOREACH
um ein Array zu durchlaufen .
Alles in allem kommt hier der Mist:Sie brauchen das meiste davon nicht.
SQL-Funktion mit rCTE
Sogar in Postgres 9.0 ein rekursiver CTE macht dies viel einfacher :
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;
Gleicher Aufruf.
Eng verwandte Antwort mit Erklärung:
SQL Fiddle demonstriert beides.