2017-05-05 49 views
3

我們如何傳遞一個(無限量的)行數組(即一個常量表)作爲PostgreSQL函數的參數/參數?如何將多行傳遞給PostgreSQL函數?

這裏有一個想法:

CREATE TYPE foo AS (
    x bigint, 
    y smallint, 
    z varchar(64) 
); 

CREATE OR REPLACE FUNCTION bar(bigint, foo[]) RETURNS TABLE(a bigint, x bigint, y smallint, z varchar(64)) AS 
$$ 
    SELECT $1, x, y, z FROM unnest($2); 
$$ 
LANGUAGE SQL; 

下面的函數調用的作品,但有沒有辦法把它縮短?

SELECT * FROM bar(1, ARRAY[(1,2,'body1'),(2,1,'body2')]::foo[]); 

例如,我們不能刪除::foo[]投,但有沒有辦法改寫的東西,使我們可以忽略它?

我們應該使用variatic參數嗎?

回答

1

PostgreSQL沒有表值變量(還沒有),所以沒有什麼是漂亮的。傳遞數組效率低下,但適用於合理大小的輸入。

對於更大的輸入,經常工作的是傳遞一個refcursor。這很笨拙,但對於較大的數據集可能很實用,有時與臨時表結合使用。

例如

CREATE OR REPLACE FUNCTION bar(i bigint, c refcursor) RETURNS TABLE(a bigint, x bigint, y smallint, z varchar(64)) AS 
$$ 
DECLARE 
    cursrow foo; 
BEGIN 
    LOOP 
     FETCH NEXT FROM c INTO cursrow; 
     a := i; 
     x := cursrow.x; 
     y := cursrow.y; 
     z := cursrow.z; 
     RETURN NEXT; 
     IF NOT FOUND THEN 
      EXIT; 
     END IF; 
    END LOOP; 
    RETURN; 
END; 
$$; 

用法:

demo=> BEGIN; 
BEGIN 
demo=> DECLARE "curs1" CURSOR FOR VALUES (1,2,'body1'), (2,1,'body2'); 
DECLARE CURSOR 
craig=> SELECT bar(1, 'curs1'); 
     bar  
--------------- 
(1,1,2,body1) 
(1,2,1,body2) 
(1,,,) 
(3 rows) 

demo=> COMMIT; 
COMMIT 

不漂亮。但是,plpgsql永遠不會。可惜的是它不具有行值左值,爲能夠寫類似(x, y, z) := cursrowROW(x, y, z) := cursrow將使其少一點醜陋。

RETURN NEXT的工作,但只有當你返回record沒有命名參數或TABLE

可悲的是,你不能使用SQL(不PLPGSQL)FETCH ALL的子表達式,所以你可以不寫

RETURN QUERY NEXT i, cursrow.* FROM (FETCH ALL FROM c) AS cursrow; 
1

看來,問題之一是使用smallint類型不能轉換隱含地來自int常量。並考慮以下因素:

-- drop function if exists bar(bigint, variadic foo[]); 
-- drop type if exists foo; 

CREATE TYPE foo AS (
    x bigint, 
    y int, -- change type to integer 
    z varchar(64) 
); 

CREATE OR REPLACE FUNCTION bar(bigint, variadic foo[]) RETURNS TABLE(
    a bigint, 
    x bigint, 
    y int, -- and here 
    z varchar(64)) AS 
$$ 
    SELECT $1, x, y, z FROM unnest($2); 
$$ 
LANGUAGE SQL; 

-- Voila! It is even simpler then the using of the ARRAY constructor 
SELECT * FROM bar(1, (1,2,'body1'), (2,1,'body2'), (3,4,'taddy bear')); 

dbfiddle

About variadic parameters