Có rất nhiều Tôi sẽ làm khác đi, và mang lại hiệu quả tuyệt vời.
Định nghĩa bảng
Bắt đầu với định nghĩa bảng và quy ước đặt tên. Đây hầu hết chỉ là ý kiến:
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
);
Những điểm chính
-
Bạn có chắc chắn cần
bigserial
cho các phòng ban? Hầu như không có nhiều người trên hành tinh này. Mộtserial
đơn giản nên đủ. -
Tôi hầu như không bao giờ sử dụng
ký tự thay đổi
với sự hạn chế về chiều dài. Không giống như với một số RDBMS khác, không có hiệu suất đạt được bằng cách sử dụng một hạn chế. ThêmKIỂM TRA
ràng buộc nếu bạn thực sự cần thực thi độ dài tối đa. Tôi chỉ sử dụngtext
, hầu hết là và tự cứu mình khỏi rắc rối. -
Tôi đề xuất một quy ước đặt tên trong đó cột khóa ngoại chia sẻ tên với cột được tham chiếu, vì vậy
master_id
thay vìmaster_fk
, v.v. Cũng cho phép sử dụngUSING
trong các lần tham gia. -
Và tôi hiếm khi sử dụng tên cột không mô tả
id
. Sử dụngdept_id
thay vào đó ở đây.
Hàm PL / pgSQL
Nó có thể được đơn giản hóa phần lớn thành:
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;
Gọi:
SELECT f_retornar_plpgsql(2, 5);
Hoặc:
SELECT f_retornar_plpgsql(VARIADIC '{2,5}');
-
ALIAS FOR $ 1
là cú pháp lỗi thời và không được khuyến khích . Sử dụng các tham số chức năng để thay thế. -
VARIADIC
để thuận tiện hơn khi gọi. Có liên quan: -
Bạn không cần
EXECUTE
cho các truy vấn không có phần tử động. Không có gì để đạt được ở đây. -
Bạn không cần xử lý ngoại lệ để tạo bảng. Trích dẫn thủ công tại đây :
-
Postgres 9.1 trở lên có
TẠO BẢNG TEMP NẾU KHÔNG TỒN TẠI
. Tôi sử dụng giải pháp thay thế cho 9.0 để tạo bảng tạm thời có điều kiện. -
Postgres 9.1 cũng cung cấp
FOREACH
để lặp qua một mảng .
Tất cả những gì đã nói, đến đây là điều đáng mừng: bạn không cần hầu hết những thứ này.
Hàm SQL với rCTE
Ngay cả trong Postgres 9.0, CTE đệ quy làm cho việc này đơn giản hơn rất nhiều :
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;
Cùng một cuộc gọi.
Câu trả lời liên quan chặt chẽ với giải thích: