2017-05-26 49 views
0

我想知道是否有可能创建一个SQL函数来对待参数行,就像它是“鸭型”一样。也就是说,我希望能够传递具有特定公用列名称的不同表或视图中的行,并在该函数内的这些列上进行操作。在SQL函数中虚拟化列

这里是一个非常简单的例子,试图说明问题:

=> CREATE TABLE tab1 (
    id   SERIAL PRIMARY KEY, 
    has_desc BOOLEAN, 
    x1   TEXT, 
    description TEXT 
); 
CREATE TABLE 
=> CREATE FUNCTION get_desc(tab tab1) RETURNS TEXT AS $$ 
    SELECT CASE tab.has_desc 
     WHEN True THEN 
      tab.description 
     ELSE 
      'Default Description' 
    END; 
$$ LANGUAGE SQL; 

=> INSERT INTO tab1 (has_desc, x1, description) VALUES (True, 'Foo', 'FooDesc'); 
INSERT 0 1 
=> INSERT INTO tab1 (has_desc, x1, description) VALUES (True, 'Bar', 'BarDesc'); 
INSERT 0 1 
=> SELECT get_desc(tab1) FROM tab1; 
get_desc 
---------- 
BarDesc 
FooDesc 
(2 rows) 

这当然是非常做作。实际上,我的表格有更多的字段,功能更复杂。

现在我想添加其他表/视图并将它们传递给相同的函数。新的表/视图具有不同的列,但函数将关心的列对于所有这些列是通用的。为了增加简单的例子,我想补充这两个表:

CREATE TABLE tab2 (
    id   SERIAL PRIMARY KEY, 
    has_desc BOOLEAN, 
    x2   TEXT, 
    description TEXT 
); 
CREATE TABLE tab3 (
    id   SERIAL PRIMARY KEY, 
    has_desc BOOLEAN, 
    x3   TEXT, 
    description TEXT 
); 

注意三个有has_descdescription字段是get_desc实际使用的唯一的。不过,当然,如果我尝试使用现有的功能与tab2,我得到:

=> select get_desc(tab2) FROM tab2; 
ERROR: function get_desc(tab2) does not exist 
LINE 1: select get_desc(tab2) FROM tab2; 
      ^
HINT: No function matches the given name and argument types. You might need to add explicit type casts. 

我希望能够定义一个共同的功能,做同样的事情get_desc但需要作为参数,从任何一个排的三张桌子。有没有办法做到这一点?

或者,还有一种方法可以将整行转换为仅包含一组定义的字段的常见行类型吗?

(我知道我可以改变函数的参数,只是采取XX.has_descXX.description,但我试图找出是哪个字段的函数内部使用,而不需要扩大这些在每一个地方的函数被调用。)

回答

0

Postgresql函数依赖于参数的模式。如果不同的表具有不同的模式,那么您总是可以将它们投影到get_desc所需的公共子模式中。这可以在get_desc使用之前以WITH子句以快速和临时的方式完成。

如果这个答案在细节上过于简单,只需添加一个评论,我会充实一些例子。

更多细节:

CREATE TABLE subschema_table (has_desc boolean, description text) ; 

CREATE FUNCTION get_desc1(tab subschema_table) RETURNS TEXT AS $$ 
    SELECT CASE tab.has_desc 
     WHEN True THEN 
      tab.description 
     ELSE 
      'Default Description' 
    END; $$ LANGUAGE SQL; 

现在,下面的工作(与其他表也):

WITH subschema AS (SELECT has_desc, description FROM tab1) 
SELECT get_desc1(subschema) FROM subschema; 

的VIEW方法并没有在我的测试工作(观点似乎不有适当的架构

也许其他答案给出了一个更好的方法

+0

我会很感激的例子。看起来,这将等同于扩展函数参数以包含函数将在其上运行的所有字段 - 如果必须在每次调用之前添加一个附加的WITH子句。 –

+0

确实如此,但就代码而言,混乱将与主查询分离。另外,使用通用模式创建每个表的VIEW也将摆脱'WITH'子句。视图可以在函数附近定义。 –

1

你可以create a cast:

CREATE CAST (tab2 AS tab1) WITH INOUT; 

INSERT INTO tab2 (has_desc, x2, description) VALUES (True, 'Bar', 'From Tab2'); 

SELECT get_desc(tab2::tab1) FROM tab2; 

get_desc 
----------- 
From Tab2 
(1 row) 
+0

在我的答案中看到警告,但对于CAST的想法+1。 –

+0

问题是关于功能冗余的兼容类型。当然,自定义演员可以是一个强大的工具。通过使用投射功能,您可以执行几乎任何完全不同类型的转换。 – klin

+0

呃,问题说:“新表/视图*有不同的列,但函数关心的列对所有列都是通用的。”我试图完成的只是将不同的行类型强制为一种我可以用作函数参数的形式。 –

1

我加入一个答案,以显示我解决了这个为后人完整的方式。但是感谢@klin让我指出了正确的方向。(@ klin的裸CAST的一个问题是,当两个表的公共列不在相应列列表中的相同相对位置上时,它不会生成正确的行类型。)

我的解决方案添加了一个包含公共字段的新自定义TYPE(gdtab),然后是一个函数,该函数可以从每个源表格的行类型转换为gdtab类型,然后添加CAST以使每个转换隐含。

-- Common type for get_desc function 
CREATE TYPE gdtab AS (
    id   INTEGER, 
    has_desc BOOLEAN, 
    description TEXT 
); 

CREATE FUNCTION get_desc(tab gdtab) RETURNS TEXT AS $$ 
    SELECT CASE tab.has_desc 
     WHEN True THEN 
      tab.description 
     ELSE 
      'Default Description' 
    END; 
$$ LANGUAGE SQL; 

CREATE TABLE tab1 (
    id   SERIAL PRIMARY KEY, 
    has_desc BOOLEAN, 
    x1   TEXT, 
    description TEXT 
); 

-- Convert tab1 rowtype to gdtab type 
CREATE FUNCTION tab1_as_gdtab(t tab1) RETURNS gdtab AS $$ 
    SELECT CAST(ROW(t.id, t.has_desc, t.description) AS gdtab); 
$$ LANGUAGE SQL; 

-- Implicitly cast from tab1 to gdtab as needed for get_desc 
CREATE CAST (tab1 AS gdtab) WITH FUNCTION tab1_as_gdtab(tab1) AS IMPLICIT; 

CREATE TABLE tab2 (
    id   SERIAL PRIMARY KEY, 
    x2   TEXT, 
    x2x   TEXT, 
    has_desc BOOLEAN, 
    description TEXT 
); 

CREATE FUNCTION tab2_as_gdtab(t tab2) RETURNS gdtab AS $$ 
    SELECT CAST(ROW(t.id, t.has_desc, t.description) AS gdtab); 
$$ LANGUAGE SQL; 

CREATE CAST (tab2 AS gdtab) WITH FUNCTION tab2_as_gdtab(tab2) AS IMPLICIT; 

测试用法:

INSERT INTO tab1 (has_desc, x1, description) VALUES (True, 'FooBlah', 'FooDesc'), 
                (False, 'BazBlah', 'BazDesc'), 
                (True, 'BarBlah', 'BarDesc'); 

INSERT INTO tab2 (has_desc, x2, x2x, description) VALUES (True, 'FooBlah', 'x2x', 'FooDesc'), 
                 (False, 'BazBlah', 'x2x', 'BazDesc'), 
                 (True, 'BarBlah', 'x2x', 'BarDesc'); 

SELECT get_desc(tab1) FROM tab1; 
SELECT get_desc(tab2) FROM tab2;