Come posso restituire più righe di record in PL / pgSQL


14

Sto cercando di restituire più record utilizzando il tipo di dati RECORD, esiste un modo per aggiungere a RECORD e aggiungere / aggiungere un nuovo valore con ogni iterazione a questo RECORD.

cioè, voglio aggiungere in recmodo che recdiventi un insieme di righe quando il ciclo è finito, che posso semplicemente RITORNARE alla fine della mia funzione. Attualmente lo sto facendo -

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

il mio codice completo è qui:

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;

Uscita corrente dopo SELECT validation();

validation
(crea_ddf,8095,f)

Uscita desiderata

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

@EvanCarroll Ciao Evan, questa è la mia domanda, che ho postato anche lì ... per ogni evenienza se qualcuno lo manca qui.
hky404,

Non sono sicuro di cosa stai cercando di fare, potresti spiegarlo un po 'di più?
Evan Carroll,

1
@ hky404: per favore non fare il cross-post; ciò provoca solo la duplicazione degli sforzi.
Martijn Pieters,

Risposte:


14

La funzione deve restituire un SETOF RECORDanziché RECORDe avere uno RETURN NEXTper riga anziché un singolo RETURN, come in:

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;

Caller:

=> seleziona * da test () come x (a int, b int);
 a | B
--- + ---
 1 | 2
 3 | 4
(2 file)

Si noti che SQL essendo fortemente e staticamente tipizzato, lo RECORDpseudo-tipo è difficile da lavorare.
Spesso è meno complicato utilizzare fin dall'inizio un tipo composito con una definizione completa di nomi e tipo per ogni colonna, sia con la TABLE(...)sintassi per un tipo anonimo sia con per un tipo con CREATE TYPEnome persistente.


8

Utilizzare setof recorde return next recse si desidera restituire più record da una funzione, ad esempio:

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

Tale funzione deve essere chiamata nella clausola FROM con un elenco di definizioni di colonne:

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)

Un'opzione migliore è usare returns table(...)e 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 $$;

Uso:

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

Questa è una bandiera rossa ..

  1. Hai un tavolo validation.
  2. Sposta le righe in una tabella temporanea staging.
  3. Tutte le righe con temp_table.col3IS FALSE vengono restituite all'utente
  4. Insieme a qualsiasi altra riga in un elenco specificato di tabelle in cui quella colonna è falsa.
  5. Quindi rilascia la tabella temporanea (su commit)

Fallo e basta ..

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;

Puoi anche inserirlo in un VIEWse lo desideri

Come nota a margine

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

Che cosa fa DISTINCTqui? Basta limitarne uno. In effetti, direi che è ancora più pulito.

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

Quindi non hai bisogno dello strano = false ) = FALSE

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.