PostgreSQL
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> PostgreSQL

Chức năng lặp lại không hoạt động như mong đợi

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ột serial đơ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êm KIỂ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ụng text , 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ụng USING 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ụng dept_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}');

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:

SQL Fiddle thể hiện cả hai.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Thứ tự đường ray theo trường liên kết

  2. thực thi chương trình bên ngoài với trình kích hoạt trong postgres 9.4

  3. Làm thế nào để chỉ ra rằng một cột không thể null khi tạo một dạng xem cụ thể hóa?

  4. Cách cài đặt PgBackRest

  5. Sư tử núi Postgres không thể kết nối