PostgreSQL
 sql >> Teknologi Basis Data >  >> RDS >> PostgreSQL

Fungsi loop in tidak berfungsi seperti yang diharapkan

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. Tambahkan CHECK kendala jika Anda benar-benar perlu menegakkan panjang maksimum. Saya hanya menggunakan text , 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 bukannya master_fk , dll. Juga memungkinkan untuk menggunakan USING dalam bergabung.

  • Dan saya jarang gunakan nama kolom nondeskriptif id . Menggunakan dept_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}');

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.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Relasi dbo.MyTable tidak ditemukan saat skema dan tabel ada

  2. Membandingkan Penyimpanan Data untuk PostgreSQL - MVCC vs InnoDB

  3. Menampilkan Data PostGIS dengan Leafletjs

  4. Menangani paging dengan mengubah urutan sortir

  5. Bagaimana cara menyimpan string yang berisi tanda kutip tunggal ke kolom teks di PostgreSQL