使用PL / pgSQL将多个字段作为PostgreSQL中的logging返回

我正在写一个SP,使用PL / pgSQL。
我想返回一个由几个不同表格的字段组成的logging。 可能看起来像这样:

CREATE OR REPLACE FUNCTION get_object_fields(name text) RETURNS RECORD AS $$ BEGIN -- fetch fields f1, f2 and f3 from table t1 -- fetch fields f4, f5 from table t2 -- fetch fields f6, f7 and f8 from table t3 -- return fields f1 ... f8 as a record END $$ language plpgsql; 

我怎样才能将不同表中的字段作为单个logging中的字段返回?

[编辑]

我意识到我上面的例子太简单了。 我需要检索的一些字段将被作为单独的行保存在被查询的数据库表中,但是我想在“拼合”logging结构中返回它们。

下面的代码应该有助于进一步说明:

 CREATE TABLE user (id int, school_id int, name varchar(32)); CREATE TYPE my_type ( user1_id int, user1_name varchar(32), user2_id int, user2_name varchar(32) ); CREATE OR REPLACE FUNCTION get_two_users_from_school(schoolid int) RETURNS my_type AS $$ DECLARE result my_type; temp_result user; BEGIN -- for purpose of this question assume 2 rows returned SELECT id, name INTO temp_result FROM user where school_id = schoolid LIMIT 2; -- Will the (pseudo)code below work?: result.user1_id := temp_result[0].id ; result.user1_name := temp_result[0].name ; result.user2_id := temp_result[1].id ; result.user2_name := temp_result[1].name ; return result ; END $$ language plpgsql 

你需要定义一个新types并定义你的函数来返回这个types。

 CREATE TYPE my_type AS (f1 varchar(10), f2 varchar(10) /* , ... */ ); CREATE OR REPLACE FUNCTION get_object_fields(name text) RETURNS my_type AS $$ DECLARE result_record my_type; BEGIN SELECT f1, f2, f3 INTO result_record.f1, result_record.f2, result_record.f3 FROM table1 WHERE pk_col = 42; SELECT f3 INTO result_record.f3 FROM table2 WHERE pk_col = 24; RETURN result_record; END $$ LANGUAGE plpgsql; 

如果要返回多个logging,则需要将该函数定义为returns setof my_type


更新

另一个select是使用RETURNS TABLE()而不是创buildPostgres 8.4中引入的TYPE

 CREATE OR REPLACE FUNCTION get_object_fields(name text) RETURNS TABLE (f1 varchar(10), f2 varchar(10) /* , ... */ ) ... 

不要使用CREATE TYPE来返回多态结果。 改为使用和滥用RECORDtypes 。 一探究竟:

 CREATE FUNCTION test_ret(a TEXT, b TEXT) RETURNS RECORD AS $$ DECLARE ret RECORD; BEGIN -- Arbitrary expression to change the first parameter IF LENGTH(a) < LENGTH(b) THEN SELECT TRUE, a || b, 'a shorter than b' INTO ret; ELSE SELECT FALSE, b || a INTO ret; END IF; RETURN ret; END;$$ LANGUAGE plpgsql; 

请注意,根据input,可以select返回列或列。

 test=> SELECT test_ret('foo','barbaz'); test_ret ---------------------------------- (t,foobarbaz,"a shorter than b") (1 row) test=> SELECT test_ret('barbaz','foo'); test_ret ---------------------------------- (f,foobarbaz) (1 row) 

这对代码造成严重破坏,因此请使用一致数量的列,但使用第一个返回操作成功的参数来返回可选错误消息是非常方便的。 使用一致的列数重写:

 CREATE FUNCTION test_ret(a TEXT, b TEXT) RETURNS RECORD AS $$ DECLARE ret RECORD; BEGIN -- Note the CASTING being done for the 2nd and 3rd elements of the RECORD IF LENGTH(a) < LENGTH(b) THEN ret := (TRUE, (a || b)::TEXT, 'a shorter than b'::TEXT); ELSE ret := (FALSE, (b || a)::TEXT, NULL::TEXT); END IF; RETURN ret; END;$$ LANGUAGE plpgsql; 

几乎到了史诗般的热度:

 test=> SELECT test_ret('foobar','bar'); test_ret ---------------- (f,barfoobar,) (1 row) test=> SELECT test_ret('foo','barbaz'); test_ret ---------------------------------- (t,foobarbaz,"a shorter than b") (1 row) 

但是,如何将其分解为多行,以便您select的ORM层可以将值转换为您select的本地数据types的语言? 热度:

 test=> SELECT a, b, c FROM test_ret('foo','barbaz') AS (a BOOL, b TEXT, c TEXT); a | b | c ---+-----------+------------------ t | foobarbaz | a shorter than b (1 row) test=> SELECT a, b, c FROM test_ret('foobar','bar') AS (a BOOL, b TEXT, c TEXT); a | b | c ---+-----------+--- f | barfoobar | (1 row) 

这是PostgreSQL中最酷和最不被使用的特性之一。 请传播这个词。

使用OUT参数可以更简单

 CREATE OR REPLACE FUNCTION get_object_fields( name text ,OUT user1_id int ,OUT user1_name varchar(32) ,OUT user2_id int ,OUT user2_name varchar(32) ) AS $func$ BEGIN SELECT t.user1_id, t.user1_name INTO user1_id, user1_name FROM tbl1 t WHERE t.tbl1_id = 42; user2_id := user1_id + 43; -- some calculation SELECT t.user2_name INTO user2_name FROM tbl2 t WHERE t.tbl2_i = user2_id; END $func$ LANGUAGE plpgsql; 
  • 您不需要为了这个plpgsql函数而创build一个types。 如果你想把几个函数绑定到同一个types,这可能会很有用。 由于添加了OUT参数,我很less再使用它了。

  • 正如您可能已经注意到的那样,没有RETURN语句。 OUT参数是自动返回的,不需要RETURN语句。

  • 由于OUT参数在函数体内部的任何地方都是可见的(并且可以像其他任何variables一样使用),请确保对同名的列进行表格限定以避免命名冲突。

更简单 – 或返回多行

大多数情况下,这可以进一步简化。 有时,函数体中的查询可以结合起来,通常(不总是)更快。 而且你可以使用RETURNS TABLE() – 和Postgres 8.4一起引入(早在这个问题被问及之前)。

上面的例子可以被重写为:

 CREATE OR REPLACE FUNCTION get_object_fields(name text) RETURNS TABLE ( user1_id int ,user1_name varchar(32) ,user2_id int ,user2_name varchar(32)) AS $func$ BEGIN RETURN QUERY SELECT t1.user1_id, t1.user1_name, t2.user2_id, t2.user2_name FROM tbl1 t1 JOIN tbl2 t2 ON t2.user2_id = t1.user1_id + 43 WHERE t1.tbl1_id = 42 LIMIT 1; -- may be optional END $func$ LANGUAGE plpgsql; 
  • RETURNS TABLE与使用RETURNS record结合的一组OUT参数实际上是相同的,只是稍微更短一些。

  • 主要区别在于,这个函数可以返回0,1行或多行,而第一个版本总是返回1行。
    如果你想确定,这个只返回0或1行,像显示的那样添加LIMIT 1

  • RETURN QUERY是直接返回查询结果的非常方便的现代方式。
    您可以在一个函数中使用多个实例将更多的行添加到输出中。

各种各样的行types

如果您的函数应该dynamic返回不同行types的结果 ,具体取决于input,请在此处阅读更多内容:

  • 重构PL / pgSQL函数以返回各种SELECT查询的输出

如果你有一个具有这个确切的logging布局的表,使用它的名字作为一个types,否则你将不得不明确声明types:

 CREATE OR REPLACE FUNCTION get_object_fields ( name text ) RETURNS mytable AS $$ DECLARE f1 INT; DECLARE f2 INT; … DECLARE f8 INT; DECLARE retval mytable; BEGIN -- fetch fields f1, f2 and f3 from table t1 -- fetch fields f4, f5 from table t2 -- fetch fields f6, f7 and f8 from table t3 retval := (f1, f2, …, f8); RETURN retval; END $$ language plpgsql; 

你可以使用OUT参数和CROSS JOIN来做到这一点

 CREATE OR REPLACE FUNCTION get_object_fields(my_name text, OUT f1 text, OUT f2 text) AS $$ SELECT t1.name, t2.name FROM table1 t1 CROSS JOIN table2 t2 WHERE t1.name = my_name AND t2.name = my_name; $$ LANGUAGE SQL; 

然后用它作为表格:

 select get_object_fields( 'Pending') ; get_object_fields ------------------- (Pending,code) (1 row) 

要么

 select * from get_object_fields( 'Pending'); f1 | f ---------+--------- Pending | code (1 row) 

要么

 select (get_object_fields( 'Pending')).f1; f1 --------- Pending (1 row)