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


14

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 à recce qui recdevient 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)
(.....)

@EvanCarroll Salut Evan, c'est ma question, que j'ai aussi postée là-bas ... juste au cas où quelqu'un la manquerait ici.
hky404

Je ne sais pas trop ce que vous essayez de faire, pourriez-vous l'expliquer un peu plus?
Evan Carroll,

1
@ hky404: veuillez ne pas effectuer de cross-post; cela ne fait que dupliquer les efforts.
Martijn Pieters

Réponses:


14

La fonction doit renvoyer un SETOF RECORDau lieu de RECORDet en avoir un RETURN NEXTpar 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 RECORDpseudo-type est difficile à travailler.
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 TABLE(...)syntaxe d'un type anonyme, soit avec CREATE TYPEun type nommé persistant.


8

Utilisez setof recordet return next recsi 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)

1

Ceci est un drapeau rouge ..

  1. Tu as une table validation.
  2. Vous déplacez les lignes dans une table temporaire staging.
  3. Toutes les lignes avec un temp_table.col3IS FALSE que vous renvoyez à 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 VIEWsi vous voulez

En note

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

Que fait DISTINCTici? 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;

Alors tu n'as pas besoin de l'étrange = false ) = FALSE

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.