sql >> Databasteknik >  >> RDS >> PostgreSQL

Loop in-funktionen fungerar inte som förväntat

Det finns mycket Jag skulle göra annorlunda, och med stor effekt.

Tabelldefinition

Börjar med tabelldefinitionen och namnkonventioner. Dessa är för det mesta bara åsikter:

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

Huvudpunkter

  • Är du säker på att du behöver en bigserial för avdelningar? Det finns knappast så många på denna planet. En vanlig serial borde räcka.

  • Jag använder nästan aldrig character varying med längdbegränsning. Till skillnad från vissa andra RDBMS finns det ingen som helst prestandavinst genom att använda en begränsning. Lägg till en CHECK begränsning om du verkligen behöver tillämpa en maximal längd. Jag använder bara text , mestadels och spara mig besväret.

  • Jag föreslår en namnkonvention där kolumnen för främmande nyckel delar namnet med den refererade kolumnen, så master_id istället för master_fk , etc. Gör det också möjligt att använda USING i går.

  • Och jag sällan använd det icke-beskrivande kolumnnamnet id . Använder dept_id istället här.

PL/pgSQL-funktion

Det kan till stor del förenklas till:

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;

Ring:

SELECT f_retornar_plpgsql(2, 5);

Eller:

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

Allt som sagt, här kommer besvikelsen:du behöver inte det mesta av det här.

SQL-funktion med rCTE

Även i Postgres 9.0, en rekursiv CTE gör det här mycket enklare :

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;

Samma samtal.

Närbesläktat svar med förklaring:

SQL-fiol som demonstrerar båda.



  1. Oracle får alla datum på en månad

  2. MySQL-GRUPP Antal

  3. Återställ en SQLite-databas

  4. Timeout för anslutning vid fråga på stort bord