Ada banyak Saya akan melakukannya secara berbeda, dan untuk efek yang luar biasa.
Definisi tabel
Dimulai dengan definisi tabel dan konvensi penamaan. Ini kebanyakan hanya opini:
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
);
Poin utama
-
Yakin memerlukan
bigserial
untuk departemen? Hampir tidak ada banyak orang di planet ini.serial
biasa sudah cukup. -
Saya hampir tidak pernah menggunakan
character varying
dengan batasan panjang. Tidak seperti beberapa RDBMS lainnya, tidak ada peningkatan kinerja apa pun dengan menggunakan batasan. TambahkanCHECK
kendala jika Anda benar-benar perlu menegakkan panjang maksimum. Saya hanya menggunakantext
, kebanyakan dan selamatkan diri saya dari masalah. -
Saya menyarankan konvensi penamaan di mana kolom kunci asing berbagi nama dengan kolom yang direferensikan, jadi
master_id
bukannyamaster_fk
, dll. Juga memungkinkan untuk menggunakanUSING
dalam bergabung. -
Dan saya jarang gunakan nama kolom nondeskriptif
id
. Menggunakandept_id
sebagai gantinya di sini.
Fungsi PL/pgSQL
Ini sebagian besar dapat disederhanakan menjadi:
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;
Telepon:
SELECT f_retornar_plpgsql(2, 5);
Atau:
SELECT f_retornar_plpgsql(VARIADIC '{2,5}');
-
ALIAS FOR $1
sintaksnya sudah usang dan tidak disarankan . Gunakan parameter fungsi sebagai gantinya. -
VARIADIC
parameter membuatnya lebih nyaman untuk menelepon. Terkait: -
Anda tidak perlu
EXECUTE
untuk kueri tanpa elemen dinamis. Tidak ada untungnya di sini. -
Anda tidak perlu penanganan pengecualian untuk membuat tabel. Mengutip manual di sini :
-
Postgres 9.1 atau yang lebih baru memiliki
CREATE TEMP TABLE IF NOT EXISTS
. Saya menggunakan solusi untuk 9.0 untuk membuat tabel temp secara kondisional. -
Postgres 9.1 juga menawarkan
FOREACH
untuk mengulang melalui array .
Semua yang dikatakan, inilah yang mengecewakan:Anda tidak membutuhkan sebagian besar dari ini.
Fungsi SQL dengan rCTE
Bahkan di Postgres 9.0, CTE rekursif membuat ini jauh lebih sederhana :
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;
Panggilan yang sama.
Jawaban terkait erat dengan penjelasan:
SQL Fiddle mendemonstrasikan keduanya.