Wie kann ich mehrere Datensatzzeilen in PL / pgSQL zurückgeben?


12

Ich versuche, mehrere Datensätze mit dem Datentyp RECORD zurückzugeben. Gibt es eine Möglichkeit, an RECORD anzuhängen und mit jeder Iteration einen neuen Wert zu diesem RECORD hinzuzufügen / anzuhängen?

Das heißt, ich möchte anhängen, recdamit nach recAblauf der Schleife eine Reihe von Zeilen entsteht, die ich am Ende meiner Funktion einfach zurückgeben kann. Derzeit mache ich das -

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

Mein vollständiger Code ist hier:

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;

Stromausgang nach SELECT validation();

validation
(crea_ddf,8095,f)

Gewünschte Ausgabe

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

@EvanCarroll Hallo Evan, das ist meine Frage, die ich auch dort gepostet habe ... nur für den Fall, dass jemand sie hier verpasst.
hky404

Ich bin mir nicht sicher, was Sie versuchen. Können Sie es etwas näher erläutern?
Evan Carroll

1
@ hky404: bitte nicht überkreuzen; das führt nur zu doppelten Anstrengungen.
Martijn Pieters

Antworten:


12

Die Funktion muss ein SETOF RECORDanstelle von RECORDund ein RETURN NEXTpro Zeile anstelle eines einzelnen zurückgeben RETURN, wie 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;

Anrufer:

=> wähle * aus test () als x (a int, b int);
 a | b
--- + ---
 1 | 2
 3 | 4
(2 Reihen)

Beachten Sie, dass SQL stark und statisch typisiert RECORDist und es schwierig ist, mit dem Pseudotyp zu arbeiten.
Oft ist es weniger umständlich, von Anfang an einen zusammengesetzten Typ mit einer vollständigen Definition von Namen und Typ für jede Spalte zu verwenden, entweder mit der TABLE(...)Syntax für einen anonymen Typ oder mit CREATE TYPEfür einen dauerhaften benannten Typ.


8

Verwenden Sie setof recordund, return next recwenn Sie mehrere Datensätze von einer Funktion zurückgeben möchten, Beispiel:

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

Eine solche Funktion muss in der FROM-Klausel mit einer Spaltendefinitionsliste aufgerufen werden:

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)

Eine bessere Option ist zu verwenden returns table(...)und 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 $$;

Verwendung:

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

Dies ist eine rote Fahne ..

  1. Du hast einen Tisch validation.
  2. Sie verschieben die Zeilen in eine temporäre Tabelle staging.
  3. Alle Zeilen mit einem temp_table.col3IS FALSE geben Sie an den Benutzer zurück
  4. Zusammen mit allen anderen Zeilen in einer angegebenen Liste von Tabellen, in denen diese Spalte falsch ist.
  5. Dann löschen Sie die temporäre Tabelle (beim Festschreiben)

Mach das einfach ..

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;

Sie können das sogar in ein setzen, VIEWwenn Sie wollen

Als Randnotiz

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

Was macht DISTINCThier? Mach einfach LIMIT eins. In der Tat würde ich argumentieren, dass dies noch sauberer ist.

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

Dann brauchst du das Seltsame nicht = false ) = FALSE

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.