Bagaimana saya bisa mengembalikan beberapa baris catatan dalam PL / pgSQL


14

Saya mencoba mengembalikan beberapa rekaman menggunakan tipe data RECORD, apakah ada cara saya dapat menambahkan ke RECORD dan menambahkan / menambahkan nilai baru dengan setiap iterasi ke RECORD ini.

yaitu, saya ingin menambahkan recsehingga recmenjadi satu set baris ketika loop selesai, yang saya dapat KEMBALI pada akhir fungsi saya. Saat ini, saya melakukan ini -

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

kode lengkap saya ada di sini:

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;

Output saat ini setelah SELECT validation();

validation
(crea_ddf,8095,f)

Output yang Diinginkan

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

@ EvanCarroll Hai Evan, itu pertanyaan saya, yang saya posting di sana juga ... untuk berjaga-jaga jika seseorang melewatkannya di sini.
hky404

Saya tidak yakin apa yang Anda coba lakukan, bisakah Anda menjelaskannya sedikit lagi?
Evan Carroll

1
@ hky404: tolong jangan posting silang; yang hanya menyebabkan duplikasi upaya.
Martijn Pieters

Jawaban:


14

Fungsi harus mengembalikan SETOF RECORDbukan RECORDdan memiliki satu RETURN NEXTper baris, bukan satu RETURN, seperti pada:

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;

Penelepon:

=> pilih * dari test () sebagai x (int, b int);
 a | b
--- + ---
 1 | 2
 3 | 4
(2 baris)

Perhatikan bahwa SQL diketik dengan kuat dan statis, tipe RECORDsemu sulit untuk dikerjakan.
Seringkali kurang praktis untuk digunakan sejak awal tipe komposit dengan definisi lengkap nama dan tipe untuk setiap kolom, baik dengan TABLE(...)sintaks untuk tipe anonim atau dengan CREATE TYPEuntuk tipe nama persisten.


8

Gunakan setof recorddan return next recjika Anda ingin mengembalikan beberapa catatan dari suatu fungsi, contoh:

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

Fungsi seperti itu perlu dipanggil dalam klausa FROM dengan daftar definisi kolom:

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)

Pilihan yang lebih baik adalah menggunakan returns table(...)dan 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 $$;

Pemakaian:

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

Ini adalah bendera merah ..

  1. Anda punya meja validation.
  2. Anda memindahkan baris ke tabel temp staging.
  3. Setiap baris dengan temp_table.col3IS SALAH Anda kembali ke pengguna
  4. Bersamaan dengan baris lain dalam daftar tabel tertentu di mana kolom itu salah.
  5. Kemudian Anda menjatuhkan tabel temp (saat melakukan)

Lakukan ini ..

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;

Anda bahkan dapat memasukkannya ke dalam VIEWjika Anda mau

Sebagai catatan

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

Apa yang dilakukan di DISTINCTsini? Cukup lakukan LIMIT satu. Bahkan, saya berpendapat bahwa ini bahkan lebih bersih.

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

Maka Anda tidak perlu yang aneh = false ) = FALSE

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.