PostgreSQL
 sql >> база данни >  >> RDS >> PostgreSQL

Функцията Loop in не работи според очакванията

Има много Бих направил различно и то с голям ефект.

Дефиниция на таблица

Започвайки с дефиницията на таблицата и конвенциите за именуване. Това са предимно само мнения:

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
);

Основни точки

  • Сигурни ли сте, че имате нужда от bigserial за отдели? Едва ли има толкова много на тази планета. Обикновен serial би трябвало да е достатъчно.

  • Почти никога не използвам character varying с ограничение на дължината. За разлика от някои други RDBMS, няма никакво увеличение на производителността чрез използване на ограничение. Добавете CHECK ограничение, ако наистина трябва да наложите максимална дължина. Използвам само text , най-вече и си спестявам неприятностите.

  • Предлагам конвенция за именуване, при която колоната с външен ключ споделя името с препратената колона, така че master_id вместо master_fk , и т.н. Също така позволява да се използва USING в обединения.

  • И аз рядко използвайте неописателното име на колона id . Използване на dept_id вместо тук.

PL/pgSQL функция

Може да се опрости до голяма степен до:

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;

Обаждане:

SELECT f_retornar_plpgsql(2, 5);

Или:

SELECT f_retornar_plpgsql(VARIADIC '{2,5}');

Всичко казано дотук идва неприятното:повечето от това не ви трябва.

SQL функция с rCTE

Дори в Postgres 9.0 рекурсивен CTE прави това много по-лесно :

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;

Същото обаждане.

Тясно свързан отговор с обяснение:

SQL Fiddle демонстрира и двете.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Архивиране и възстановяване на PostgreSQL база данни и настройка на локална хост среда с laravel в Windows 7

  2. В Rails не можах да създадем база данни за {adapter=>postgresql,

  3. PostgreSQL деактивира повече изход

  4. Надстройте PostgreSQL от 9.6 до 10.0 на Ubuntu 16.10

  5. Показване на изображение от база данни PostgreSQL, bytea