我有多个表,每两行都感兴趣:connection_node_start_id
和 connection_node_end_id
。我的目标是获取所有这些 ID 的集合,可以是平面 ARRAY
形式,也可以是由一行组成的新 TABLE
形式。
示例输出数组:
result = {1,4,7,9,2,5}
示例输出表:
IDS
-------
1
4
7
9
2
5
我的第一次尝试有点笨拙,并且无法正常工作,因为 SELECT
语句仅返回一行。看来必须有一个简单的方法来做到这一点,有人可以指出我正确的方向吗?
CREATE OR REPLACE FUNCTION get_connection_nodes(anyarray)
RETURNS anyarray AS
$$
DECLARE
table_name varchar;
result integer[];
sel integer[];
BEGIN
FOREACH table_name IN ARRAY $1
LOOP
RAISE NOTICE 'table_name(%)',table_name;
EXECUTE 'SELECT ARRAY[connection_node_end_id,
connection_node_start_id] FROM ' || table_name INTO sel;
RAISE NOTICE 'sel(%)',sel;
result := array_cat(result, sel);
END LOOP;
RETURN result;
END
$$
LANGUAGE 'plpgsql';
测试表:
connection_node_start_id | connection_node_end_id
--------------------------------------------------
1 | 4
7 | 9
调用:
SELECT get_connection_nodes(ARRAY['test_table']);
结果:
{1,4} -- only 1st row, rest is missing
最佳答案
CREATE OR REPLACE FUNCTION get_connection_nodes(text[])
RETURNS TABLE (ids int)
LANGUAGE plpgsql AS
$func$
DECLARE
_tbl text;
BEGIN
FOREACH _tbl IN ARRAY $1
LOOP
RETURN QUERY EXECUTE format('
SELECT t.id
FROM %I, LATERAL (VALUES (connection_node_start_id)
, (connection_node_end_id)) t(id)'
, _tbl);
END LOOP;
END
$func$;
dba.SE 上的相关答案:
或者删除循环并连接单个查询。可能最快:
CREATE OR REPLACE FUNCTION get_connection_nodes2(text[])
RETURNS TABLE (ids int)
LANGUAGE plpgsql AS
$func$
BEGIN
RETURN QUERY EXECUTE (
SELECT string_agg(format(
'SELECT t.id FROM %I, LATERAL (VALUES (connection_node_start_id)
, (connection_node_end_id)) t(id)'
, tbl), ' UNION ALL ')
FROM unnest($1) tbl
);
END
$func$;
相关:
LATERAL
是随 Postgres 9.3 引入的。
对于非常旧的 Postgres 版本
您也可以在 SELECT
列表中使用设置返回函数 unnest()
:
CREATE OR REPLACE FUNCTION get_connection_nodes2(text[])
RETURNS TABLE (ids int)
LANGUAGE plpgsql AS
$func$
BEGIN
RETURN QUERY EXECUTE (
SELECT string_agg(
'SELECT unnest(ARRAY[connection_node_start_id
, connection_node_end_id]) FROM ' || tbl
, ' UNION ALL '
)
FROM (SELECT quote_ident(tbl) AS tbl FROM unnest($1) tbl) t
);
END
$func$;
应该适用于 8.4+ pg(或者甚至更老)。也适用于当前的 Postgres (9.4),但 LATERAL
更简洁。
或者让它非常简单:
CREATE OR REPLACE FUNCTION get_connection_nodes3(text[])
RETURNS TABLE (ids int)
LANGUAGE plpgsql AS
$func$
BEGIN
RETURN QUERY EXECUTE (
SELECT string_agg(format(
'SELECT connection_node_start_id FROM %1$I
UNION ALL
SELECT connection_node_end_id FROM %1$I'
, tbl), ' UNION ALL ')
FROM unnest($1) tbl
);
END
$func$;
format()
是在 9.1 页中引入的。
对于大表可能会慢一些,因为每个表的每一列都会扫描一次(所以这里扫描两次)。结果中的排序顺序也不同 - 但这对您来说似乎并不重要。
请务必清理转义标识符,以防止 SQL 注入(inject)和其他非法语法。详情:
关于postgresql - 从多个表中的多个列中获取 ID 作为一组或数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31938419/