ฉันจะคืนค่าเรคคอร์ดหลายแถวใน PL / pgSQL ได้อย่างไร


14

ฉันกำลังพยายามส่งคืนหลายระเบียนโดยใช้ชนิดข้อมูล RECORD มีวิธีที่ฉันสามารถผนวกเข้ากับ RECORD และเพิ่ม / ผนวกค่าใหม่ด้วยการวนซ้ำแต่ละครั้งในการบันทึกนี้

นั่นคือฉันต้องการผนวกrecเพื่อที่recจะกลายเป็นชุดของแถวเมื่อวนซ้ำซึ่งฉันสามารถกลับมาที่ส่วนท้ายของฟังก์ชั่นของฉัน ขณะนี้ฉันกำลังทำสิ่งนี้ -

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

รหัสเต็มของฉันอยู่ที่นี่:

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;

กระแสไฟหลัง SELECT validation();

validation
(crea_ddf,8095,f)

ผลลัพธ์ที่ต้องการ

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

@EvanCarroll สวัสดี Evan นั่นคือคำถามของฉันซึ่งฉันโพสต์ตรงนั้นเช่นกัน ... ในกรณีที่มีคนคิดถึงมันที่นี่
hky404

ฉันไม่แน่ใจว่าคุณกำลังพยายามทำอะไรคุณช่วยอธิบายอีกเล็กน้อยได้ไหม?
Evan Carroll

1
@ hky404: โปรดอย่าโพสต์ ที่ทำให้เกิดความซ้ำซ้อนของความพยายามเท่านั้น
Martijn Pieters

คำตอบ:


14

ฟังก์ชั่นความต้องการที่จะกลับมาเป็นSETOF RECORDแทนRECORDและมีหนึ่งRETURN NEXTต่อแถวแทนเดียวRETURNเช่น:

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:

=> select * จาก test () เป็น x (a, b int);
 a | ข
--- + ---
 1 | 2
 3 | 4
(2 แถว)

โปรดทราบว่า SQL จะถูกพิมพ์อย่างรุนแรงและแบบคงที่RECORDประเภทหลอกยากที่จะทำงานกับ
บ่อยครั้งที่มันยุ่งยากน้อยกว่าในการใช้งานตั้งแต่เริ่มต้นประเภทคอมโพสิตที่มีคำจำกัดความเต็มรูปแบบของชื่อและประเภทสำหรับแต่ละคอลัมน์ไม่ว่าจะด้วยTABLE(...)ไวยากรณ์สำหรับประเภทที่ไม่ระบุชื่อหรือCREATE TYPEสำหรับประเภทที่มีชื่อถาวร


8

ใช้setof recordและreturn next recหากคุณต้องการส่งกลับหลายระเบียนจากฟังก์ชั่นตัวอย่าง:

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

ฟังก์ชันดังกล่าวจำเป็นต้องถูกเรียกใช้ในส่วนคำสั่ง FROM พร้อมกับรายการนิยามคอลัมน์:

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)

ตัวเลือกที่ดีกว่าคือการใช้returns table(...)และ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 $$;

การใช้งาน:

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

นี่คือธงสีแดง ..

  1. คุณมีโต๊ะ validationคุณมีตาราง
  2. คุณย้ายแถวลงในตารางชั่วคราว stagingคุณย้ายแถวลงในตารางชั่วคราว
  3. แถวใดก็ได้ด้วย temp_table.col3เป็น FALSE คุณจะกลับสู่ผู้ใช้
  4. พร้อมกับแถวอื่น ๆ ในรายการตารางที่ระบุที่คอลัมน์นั้นเป็นเท็จ
  5. จากนั้นคุณวางตาราง temp (เมื่อกระทำ)

ทำแค่นี้ ..

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;

คุณสามารถใส่ลงไปใน a VIEWถ้าคุณต้องการ

ในฐานะที่เป็นบันทึกด้านข้าง

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

ที่DISTINCTนี่ทำอะไร ทำได้แค่ขีด จำกัด หนึ่งตัว ในความเป็นจริงฉันจะยืนยันว่านี่เป็นสิ่งที่สะอาดกว่า

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

ถ้าอย่างนั้นคุณไม่ต้องการสิ่งแปลก ๆ = false ) = FALSE

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.