sky*_*gle 64 sql postgresql types stored-procedures plpgsql
我正在使用PL/pgSQL编写SP.
我想返回一个记录,由几个不同的表中的字段组成.看起来像这样:
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;
Run Code Online (Sandbox Code Playgroud)
如何将不同表中的字段作为单个记录中的字段返回?
[编辑]
我已经意识到我上面给出的例子有点过于简单了.我需要检索的一些字段将被保存为要查询的数据库表中的单独行,但我想在"展平"记录结构中返回它们.
下面的代码应该有助于进一步说明:
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
Run Code Online (Sandbox Code Playgroud)
Sea*_*ean 106
不要使用CREATE TYPE返回多态结果.请改为使用和滥用RECORD类型.看看这个:
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;
Run Code Online (Sandbox Code Playgroud)
请注意,根据输入,它可以选择返回两列或三列.
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)
Run Code Online (Sandbox Code Playgroud)
这确实会对代码造成严重破坏,因此请使用一致数量的列,但使用第一个返回操作成功的参数返回可选错误消息非常方便.使用一致数量的列重写:
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;
Run Code Online (Sandbox Code Playgroud)
几乎是史诗般的热度:
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)
Run Code Online (Sandbox Code Playgroud)
但是,如何将其拆分为多行,以便您选择的ORM层可以将值转换为您选择的本机数据类型的语言?热度:
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)
Run Code Online (Sandbox Code Playgroud)
这是PostgreSQL中最酷最少使用的功能之一.请传播这个词.
a_h*_*ame 58
您需要定义一个新类型并定义函数以返回该类型.
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;
Run Code Online (Sandbox Code Playgroud)
如果要返回多个记录,则需要将该函数定义为 returns setof my_type
更新
另一个选择是使用RETURNS TABLE()而不是创建TYPEPostgres 8.4中引入的
CREATE OR REPLACE FUNCTION get_object_fields(name text)
RETURNS TABLE (f1 varchar(10), f2 varchar(10) /* , ... */ )
...
Run Code Online (Sandbox Code Playgroud)
Erw*_*ter 49
这可以简单用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;
Run Code Online (Sandbox Code Playgroud)
你并不需要,只是这种PLPGSQL功能的缘故创建一个类型.如果要将几个函数绑定到同一类型,这可能很有用.我很少再使用它,因为OUT添加了参数.
您可能已经注意到,没有RETURN声明.OUT参数自动返回,无需RETURN声明.
由于OUT参数在函数体内的任何位置都可见(并且可以像任何其他变量一样使用),因此请确保对同名的列进行表限定以避免命名冲突.
大多数情况下,这可以进一步简化.有时可以组合函数体中的查询,这通常(并非总是)更快.并且您可以使用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;
Run Code Online (Sandbox Code Playgroud)
RETURNS TABLE实际上与将一堆OUT参数组合在一起RETURNS record,只是更短/更优雅.
主要区别在于,此函数可以返回0,1行或多行,而第一个版本始终返回1行.
如果你想确定,这个只返回0或1行,添加LIMIT 1如演示.
RETURN QUERY是直接从查询返回结果的非常方便的现代方法.
您可以在单个函数中使用多个实例来向输出添加更多行.
如果您的函数应该动态返回具有不同行类型的结果,具体取决于输入,请在此处阅读更多内容:
如果您有一个具有此确切记录布局的表,请将其名称用作类型,否则您必须明确声明该类型:
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;
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
141387 次 |
| 最近记录: |