web-dev-qa-db-fra.com

Comment puis-je retourner plusieurs lignes d'enregistrements en PL / pgSQL

J'essaie de renvoyer plusieurs enregistrements en utilisant le type de données RECORD, est-il possible d'ajouter à RECORD et d'ajouter/ajouter une nouvelle valeur à chaque itération à ce RECORD.

c'est-à-dire que je veux ajouter à rec pour que rec devienne un ensemble de lignes lorsque la boucle est terminée, que je peux simplement RETOURNER à la fin de ma fonction. Actuellement, je fais cela -

SELECT temp_table.col1, temp_table.col2, temp_table.col3
      INTO rec
      FROM temp_table
      WHERE temp_table.col3 = false;

mon code complet est ici:

CREATE OR REPLACE FUNCTION validation()
  RETURNS RECORD AS $$
DECLARE
        rec RECORD;
        temp_row RECORD;
BEGIN

  CREATE TEMPORARY TABLE temp_table (col1 TEXT, col2 INTEGER, col3 BOOLEAN) ON COMMIT DROP;

  FOR temp_row IN SELECT * FROM staging.validation
  LOOP

    RAISE NOTICE 'sql: %', temp_row.sql;

    EXECUTE format('INSERT INTO temp_table %s', temp_row.sql);

    IF (SELECT DISTINCT temp_table.col3 FROM temp_table WHERE temp_table.col3 = false)=false THEN
      RAISE NOTICE 'there is a false value';

      SELECT temp_table.col1, temp_table.col2, temp_table.col3
      INTO rec
      FROM temp_table
      WHERE temp_table.col3 = false;
    END IF;


  END LOOP;
  RETURN rec;
END; $$
LANGUAGE plpgsql;

Sortie courant après SELECT validation();

validation
(crea_ddf,8095,f)

Sortie désirée

validation
(crea_ddf,8095,f)
(some_source_system,some_count,f)
(some_other_source_system,some_count,f)
(.....)
14
hky404

La fonction doit renvoyer un SETOF RECORD Au lieu de RECORD et avoir un RETURN NEXT Par ligne au lieu d'un seul RETURN, comme dans:

CREATE FUNCTION test() RETURNS SETOF RECORD AS $$
DECLARE
 rec record;
BEGIN
  select 1,2 into rec;
  return next rec;

  select 3,4 into rec;
  return next rec;
END $$ language plpgsql;

Votre interlocuteur:

 => sélectionnez * dans test () comme x (a int, b int); 
 a | b 
 --- + --- 
 1 | 2 
 3 | 4 
 (2 rangées) 
 

Notez que SQL étant fortement et statiquement typé, le pseudo-type RECORD est difficile à utiliser.
Souvent, il est moins lourd d'utiliser dès le départ un type composite avec une définition complète des noms et du type pour chaque colonne, soit avec la syntaxe TABLE(...) pour un type anonyme, soit avec CREATE TYPE pour un type nommé persistant.

14
Daniel Vérité

Utilisez setof record Et return next rec Si vous souhaitez renvoyer plusieurs enregistrements d'une fonction, par exemple:

create or replace function test_function()
    returns setof record 
    language plpgsql as $$
declare
    rec record;
begin
    for rec in
        select i, format('str%s', i), i/2*2 = i
        from generate_series(1, 3) i
    loop
        return next rec;
    end loop;
end $$;

Une telle fonction doit être appelée dans la clause FROM avec une liste de définitions de colonnes:

select test_function(); -- NO

ERROR:  set-valued function called in context that cannot accept a set  

select * from test_function();  -- NO

ERROR:  a column definition list is required for functions returning "record"

select * from test_function() as (id int, str text, is_even boolean);

 id | str  | is_even 
----+------+---------
  1 | str1 | f
  2 | str2 | t
  3 | str3 | f
(3 rows)

Une meilleure option consiste à utiliser returns table(...) et return query:

drop function if exists test_function();
create or replace function test_function()
    returns table (id int, str text, is_even boolean)
    language plpgsql as $$
begin
    return query
        select i, format('str%s', i), i/2*2 = i
        from generate_series(1, 3) i;
    -- you can use return query multiple times
    -- or assign values to columns
    -- and return the row:
    id = 100;
    str = 'extra';
    is_even = true;
    return next; -- without a parameter
end $$;

Usage:

select test_function(); -- possible but rather impractical

 test_function 
---------------
 (1,str1,f)
 (2,str2,t)
 (3,str3,f)
 (100,extra,t)
(4 rows)

select * from test_function();

 id  |  str  | is_even 
-----+-------+---------
   1 | str1  | f
   2 | str2  | t
   3 | str3  | f
 100 | extra | t
(4 rows)
8
klin

Ceci est un drapeau rouge ..

  1. Vous avez une table validation.
  2. Vous déplacez les lignes dans une table temporaire staging.
  3. Toutes les lignes avec un temp_table.col3 IS FALSE vous retournez à l'utilisateur
  4. Avec toutes les autres lignes d'une liste spécifiée de tables où cette colonne est fausse.
  5. Ensuite, vous supprimez la table temporaire (lors de la validation)

Faites juste ça ..

WITH t AS ( SELECT true AS runthis FROM staging.validation WHERE col3 IS FALSE )
SELECT *
FROM staging.validation
WHERE t.runthis && col3 = 3
UNION ALL 
  SELECT *
  FROM some_source_system
  WHERE t.runthis && some_source_system.col3 = 3
UNION ALL 
  SELECT *
  FROM some_other_source_system
  WHERE t.runthis && some_other_source_system.col3 = 3;

Vous pouvez même mettre ça dans un VIEW si vous voulez

En note

SELECT DISTINCT temp_table.col3
FROM temp_table
WHERE temp_table.col3 = false

Que fait DISTINCT ici? Il suffit d'en limiter un. En fait, je dirais que c'est encore plus propre.

SELECT true
FROM temp_table
WHERE temp_table.col3 = false
LIMIT 1;

Ensuite, vous n'avez pas besoin de l'étrange = false ) = FALSE

1
Evan Carroll