sql >> Database >  >> RDS >> PostgreSQL

Loop in-functie werkt niet zoals verwacht

Er is veel Ik zou het anders doen, en met groot effect.

Tabeldefinitie

Beginnend met de tabeldefinitie en naamgevingsconventies. Dit zijn meestal slechts meningen:

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

Belangrijkste punten

  • Weet je zeker dat je een bigserial voor afdelingen? Er zijn er nauwelijks zoveel op deze planeet. Een gewone serial zou voldoende moeten zijn.

  • Ik gebruik zelden karakter variërende met een lengtebeperking. In tegenstelling tot sommige andere RDBMS is er geen enkele prestatiewinst door het gebruik van een beperking. Voeg een CHECK toe beperking als u echt een maximale lengte moet afdwingen. Ik gebruik gewoon text , meestal en bespaar mezelf de moeite.

  • Ik stel een naamgevingsconventie voor waarbij de kolom met de refererende sleutel de naam deelt met de kolom waarnaar wordt verwezen, dus master_id in plaats van master_fk , etc. Staat ook toe om USING . te gebruiken in samenvoegingen.

  • En ik zelden gebruik de niet-beschrijvende kolomnaam id . dept_id gebruiken in plaats daarvan hier.

PL/pgSQL-functie

Het kan grotendeels worden vereenvoudigd tot:

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;

Bel:

SELECT f_retornar_plpgsql(2, 5);

Of:

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

Dat gezegd hebbende, komt hier het minpunt:je hebt het meeste hiervan niet nodig.

SQL-functie met rCTE

Zelfs in Postgres 9.0, een recursieve CTE maakt dit een stuk eenvoudiger :

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;

Zelfde oproep.

Nauw verwant antwoord met uitleg:

SQL Fiddle die beide demonstreert.



  1. Python:niet-ondersteund formaatteken ''' (0x27) bij index 350

  2. MySQL selecteer na rij op specifieke regels

  3. Uitzondering bij het laden van JDBC-ODBC-stuurprogramma

  4. Kan MySQL FULL JOIN-query niet verwerken