Postgres 函数:return 多个表
Postgres function: return multiple tables
是否可以从 postgres 函数 return 多个不同类型的结果集?
类似于:
CREATE OR REPLACE FUNCTION getUserById()
RETURNS setof ???
AS
$$
BEGIN
return query select id, name /* and other columns */ from users where id = 1;
return query select id, phone_number from user_phones where user_id = 1
END
$$
LANGUAGE plpgsql;
我不想使用连接,因为用户可以使用多个电话。避免使用游标也很好。
在 MS SQL 中是可能的,我想在 postgres 中做同样的事情。
经过搜索,找不到更好的使用游标的解决方案。
CREATE FUNCTION load_page(_session INT) RETURNS setof refcursor AS
$$
DECLARE c_top_items refcursor;
DECLARE c_shopping_cart refcursor;
BEGIN
OPEN c_top_items FOR
SELECT t.name, t.description
FROM top_item t
ORDER BY t.popularity DESC
LIMIT 10;
RETURN NEXT c_top_items;
OPEN c_shopping_cart FOR
SELECT c.product_id, c.product_name, c.quantity
FROM shopping_cart c
WHERE c.session_id = _session
ORDER BY c.id;
RETURN NEXT c_shopping_cart;
END;
$$ LANGUAGE plpgsql;
并调用:
BEGIN;
SELECT load_page(mySession);
FETCH ALL IN "<server cursor 1>";
FETCH ALL IN "<server cursor 2>";
COMMIT;
I don't want to use joins because several phones for user are possible.
这不是避免在 PostgreSQL 中使用 JOIN
的理由。完全没有。
PostgreSQL 允许您将 phone 个数字聚合到一个数组中:
CREATE OR REPLACE FUNCTION getUserById()
RETURNS TABLE (
id INTEGER,
name TEXT,
/* and other columns */
phone_numbers TEXT[]
)
AS
$$
select
users.id,
users.name,
/* and other columns */
-- Remove NULL because you get an array containing just NULL
-- if user_phones doesn't contain any matching rows.
array_remove(array_agg(user_phones.phone_number), NULL) as phone_numbers
from users
left join user_phones on user_phones.user_id = users.id
where users.id = 1
-- Note that grouping by a table's primary key allows you to use
-- any column from that table in the select in PostgreSQL
group by users.id
;
$$
LANGUAGE SQL
STABLE
;
这样更简单直观。
如果可以为没有 phone 号码的用户返回零行,您可以切换到内部联接。在这种情况下,您可以挂断 array_remove
呼叫。
我还向函数添加了 STABLE
规范(因为它不修改任何 table 数据)并将其切换为 SQL
而不是 PGPLSQL
(因为它只是一个查询)。这样可以让PG更好的优化;特别是,在某些情况下,它可以内联查询并下推过滤器。实际上,您甚至可能不需要功能。
CREATE OR REPLACE FUNCTION public.TestReturnMultipleTales
(
param_coid integer,
ref1 refcursor,
ref2 refcursor
)
RETURNS SETOF refcursor
LANGUAGE 'plpgsql'
COST 100
VOLATILE PARALLEL UNSAFE
ROWS 1000
AS $BODY$
DECLARE
BEGIN
OPEN ref1 FOR SELECT * FROM dbo.tbl1 WHERE coid = param_coid;
RETURN NEXT ref1;
OPEN ref2 FOR SELECT * FROM dbo.tbl2 LIMIT 5;
RETURN NEXT ref2;
END;
$BODY$;
BEGIN;
SELECT football_players.show_cities_multiple(123456, 'Ref1', 'Ref2');
FETCH ALL IN "Ref1";
FETCH ALL IN "Ref2";
COMMIT;
此外,如果想使用 C# 将其实施到 .NET 中,请与我联系我已解决问题。
你也可以使用string_agg
,那么就不用担心null
的情况了。
CREATE OR REPLACE FUNCTION getUserById()
RETURNS table (___id int, ___name text, __phone_numbers text)
AS
$$
BEGIN
return query
select id, name, STRING_AGG (phone_number,',') phone_numbers
from users u join user_phones up on u.id = up.user_id
where u.id = 1
group by 1,2;
END
$$
LANGUAGE plpgsql;
这个有效
CREATE OR REPLACE FUNCTION public.func_test(id integer ,ref1 refcursor,ref2 refcursor)
RETURNS SETOF refcursor
LANGUAGE plpgsql
AS $function$
BEGIN
OPEN ref1 FOR SELECT * FROM table1;
RETURN NEXT ref1;
OPEN ref2 FOR SELECT * FROM table2;
RETURN NEXT ref2;
END;
$function$
;
在sql
中执行
BEGIN;
SELECT func_test(69, 'Ref1', 'Ref2');
FETCH ALL IN "Ref1";
FETCH ALL IN "Ref2";
COMMIT;
在python
中执行
import psycopg2
conn = psycopg2.connect(database = "name", user = "user", password = "pass", host = "127.0.0.1", port = "5432")
cur = conn.cursor()
cur.execute("SELECT * FROM func_test(69, 'Ref1', 'Ref2');")
cur.execute('FETCH ALL IN "Ref1";')
tbl1 = cur.fetchall()
print(tbl1)
cur.execute('FETCH ALL IN "Ref1";')
tbl2 = cur.fetchall()
print(tbl2)
干杯!
Salatti Kopi Kara
是否可以从 postgres 函数 return 多个不同类型的结果集?
类似于:
CREATE OR REPLACE FUNCTION getUserById()
RETURNS setof ???
AS
$$
BEGIN
return query select id, name /* and other columns */ from users where id = 1;
return query select id, phone_number from user_phones where user_id = 1
END
$$
LANGUAGE plpgsql;
我不想使用连接,因为用户可以使用多个电话。避免使用游标也很好。 在 MS SQL 中是可能的,我想在 postgres 中做同样的事情。
经过搜索,找不到更好的使用游标的解决方案。
CREATE FUNCTION load_page(_session INT) RETURNS setof refcursor AS
$$
DECLARE c_top_items refcursor;
DECLARE c_shopping_cart refcursor;
BEGIN
OPEN c_top_items FOR
SELECT t.name, t.description
FROM top_item t
ORDER BY t.popularity DESC
LIMIT 10;
RETURN NEXT c_top_items;
OPEN c_shopping_cart FOR
SELECT c.product_id, c.product_name, c.quantity
FROM shopping_cart c
WHERE c.session_id = _session
ORDER BY c.id;
RETURN NEXT c_shopping_cart;
END;
$$ LANGUAGE plpgsql;
并调用:
BEGIN;
SELECT load_page(mySession);
FETCH ALL IN "<server cursor 1>";
FETCH ALL IN "<server cursor 2>";
COMMIT;
I don't want to use joins because several phones for user are possible.
这不是避免在 PostgreSQL 中使用 JOIN
的理由。完全没有。
PostgreSQL 允许您将 phone 个数字聚合到一个数组中:
CREATE OR REPLACE FUNCTION getUserById()
RETURNS TABLE (
id INTEGER,
name TEXT,
/* and other columns */
phone_numbers TEXT[]
)
AS
$$
select
users.id,
users.name,
/* and other columns */
-- Remove NULL because you get an array containing just NULL
-- if user_phones doesn't contain any matching rows.
array_remove(array_agg(user_phones.phone_number), NULL) as phone_numbers
from users
left join user_phones on user_phones.user_id = users.id
where users.id = 1
-- Note that grouping by a table's primary key allows you to use
-- any column from that table in the select in PostgreSQL
group by users.id
;
$$
LANGUAGE SQL
STABLE
;
这样更简单直观。
如果可以为没有 phone 号码的用户返回零行,您可以切换到内部联接。在这种情况下,您可以挂断 array_remove
呼叫。
我还向函数添加了 STABLE
规范(因为它不修改任何 table 数据)并将其切换为 SQL
而不是 PGPLSQL
(因为它只是一个查询)。这样可以让PG更好的优化;特别是,在某些情况下,它可以内联查询并下推过滤器。实际上,您甚至可能不需要功能。
CREATE OR REPLACE FUNCTION public.TestReturnMultipleTales
(
param_coid integer,
ref1 refcursor,
ref2 refcursor
)
RETURNS SETOF refcursor
LANGUAGE 'plpgsql'
COST 100
VOLATILE PARALLEL UNSAFE
ROWS 1000
AS $BODY$
DECLARE
BEGIN
OPEN ref1 FOR SELECT * FROM dbo.tbl1 WHERE coid = param_coid;
RETURN NEXT ref1;
OPEN ref2 FOR SELECT * FROM dbo.tbl2 LIMIT 5;
RETURN NEXT ref2;
END;
$BODY$;
BEGIN;
SELECT football_players.show_cities_multiple(123456, 'Ref1', 'Ref2');
FETCH ALL IN "Ref1";
FETCH ALL IN "Ref2";
COMMIT;
此外,如果想使用 C# 将其实施到 .NET 中,请与我联系我已解决问题。
你也可以使用string_agg
,那么就不用担心null
的情况了。
CREATE OR REPLACE FUNCTION getUserById()
RETURNS table (___id int, ___name text, __phone_numbers text)
AS
$$
BEGIN
return query
select id, name, STRING_AGG (phone_number,',') phone_numbers
from users u join user_phones up on u.id = up.user_id
where u.id = 1
group by 1,2;
END
$$
LANGUAGE plpgsql;
这个有效
CREATE OR REPLACE FUNCTION public.func_test(id integer ,ref1 refcursor,ref2 refcursor)
RETURNS SETOF refcursor
LANGUAGE plpgsql
AS $function$
BEGIN
OPEN ref1 FOR SELECT * FROM table1;
RETURN NEXT ref1;
OPEN ref2 FOR SELECT * FROM table2;
RETURN NEXT ref2;
END;
$function$
;
在sql
中执行BEGIN;
SELECT func_test(69, 'Ref1', 'Ref2');
FETCH ALL IN "Ref1";
FETCH ALL IN "Ref2";
COMMIT;
在python
中执行import psycopg2
conn = psycopg2.connect(database = "name", user = "user", password = "pass", host = "127.0.0.1", port = "5432")
cur = conn.cursor()
cur.execute("SELECT * FROM func_test(69, 'Ref1', 'Ref2');")
cur.execute('FETCH ALL IN "Ref1";')
tbl1 = cur.fetchall()
print(tbl1)
cur.execute('FETCH ALL IN "Ref1";')
tbl2 = cur.fetchall()
print(tbl2)
干杯!
Salatti Kopi Kara