Hay mucho Yo lo haría diferente y con gran efecto.
Definición de tabla
Comenzando con la definición de la tabla y las convenciones de nomenclatura. Estas son en su mayoría solo opiniones:
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
);
Puntos principales
-
¿Está seguro de que necesita un
bigserial
para departamentos? Apenas hay tantos en este planeta. Un simpleserial
debería ser suficiente. -
Casi nunca uso
variación de caracteres
con una restricción de longitud. A diferencia de otros RDBMS, no hay ganancia alguna en el rendimiento al usar una restricción. Agrega unCHEQUE
restricción si realmente necesita imponer una longitud máxima. Solo usotext
, principalmente y ahórrate el problema. -
Sugiero una convención de nomenclatura en la que la columna de clave externa comparte el nombre con la columna a la que se hace referencia, por lo que
master_id
en lugar demaster_fk
, etc. También permite usarUSING
en uniones. -
Y yo rara vez use el nombre de columna no descriptivo
id
. Usandodept_id
en cambio aquí.
Función PL/pgSQL
Se puede simplificar en gran medida a:
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;
Llamar:
SELECT f_retornar_plpgsql(2, 5);
O:
SELECT f_retornar_plpgsql(VARIADIC '{2,5}');
-
ALIAS POR $1
es una sintaxis obsoleta y desaconsejable . Utilice parámetros de función en su lugar. -
El
VARIÁDICO
El parámetro hace que sea más conveniente llamar. Relacionado: -
No necesitas
EJECUTAR
para consultas sin elementos dinámicos. No hay nada que ganar aquí. -
No necesita el manejo de excepciones para crear una tabla. Citando el manual aquí :
-
Postgres 9.1 o posterior tiene
CREATE TABLA TEMPORAL SI NO EXISTE
. Utilizo una solución para 9.0 para crear condicionalmente la tabla temporal. -
Postgres 9.1 también ofrece
FOREACH
para recorrer una matriz .
Dicho todo esto, aquí viene el fastidio:no necesitas la mayor parte de esto.
Función SQL con rCTE
Incluso en Postgres 9.0, un CTE recursivo hace esto mucho más simple :
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;
Misma llamada.
Respuesta estrechamente relacionada con explicación: