我有两个感兴趣的行:connection_node_start_id
和connection_node_end_id
。我的目标是获取所有这些ID的集合,可以是平面ARRAY
,也可以是由一行组成的新TABLE
。
示例输出ARRAY:
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
答案 0 :(得分:1)
EXECUTE ... INTO
语句只能从单行返回数据:
如果返回多行,则只会将第一行分配给INTO变量。
为了连接所有行的值,你必须先按列聚合它们,然后追加数组:
EXECUTE 'SELECT array_agg(connection_node_end_id) ||
array_agg(connection_node_start_id) FROM ' || table_name INTO sel;
答案 1 :(得分:1)
CREATE OR REPLACE FUNCTION get_connection_nodes(text[])
RETURNS TABLE (ids int) 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$ LANGUAGE plpgsql;
关于dba.SE的相关答案:
或者删除循环并连接单个查询。可能 最快 :
CREATE OR REPLACE FUNCTION get_connection_nodes2(text[])
RETURNS TABLE (ids int) 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$ LANGUAGE plpgsql;
相关:
Postgres 9.3 引入了您也可以在SELECT列表中使用set-returning函数unnest()
:
CREATE OR REPLACE FUNCTION get_connection_nodes2(text[])
RETURNS TABLE (ids int) 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$ LANGUAGE plpgsql;
应该使用pg 8.4+(或者甚至更老)。适用于当前的Postgres(9.4),但LATERAL
更清晰。
或者 非常简单 :
CREATE OR REPLACE FUNCTION get_connection_nodes3(text[])
RETURNS TABLE (ids int) 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$ LANGUAGE plpgsql;
pg 9.1引入了
对于大表可能会慢一点,因为每个表每列扫描一次(这里是2次)。结果中的排序顺序也不同 - 但这对您来说似乎并不重要。
确保清理转义标识符以防止SQL注入和其他非法语法。详细说明:
答案 2 :(得分:1)
你可能正在寻找这样的东西:
CREATE OR REPLACE FUNCTION d (tblname TEXT [])
RETURNS TABLE (c INTEGER) AS $$
DECLARE sql TEXT;
BEGIN
WITH x
AS (SELECT unnest(tblname) AS tbl),
y AS (
SELECT FORMAT('
SELECT connection_node_end_id
FROM %s
UNION ALL
SELECT connection_node_start_id
FROM %s
', tbl, tbl) AS s
FROM x)
SELECT string_agg(s, ' UNION ALL ')
INTO sql
FROM y;
RETURN QUERY EXECUTE sql;
END;$$
LANGUAGE plpgsql;
CREATE TABLE a (connection_node_end_id INTEGER, connection_node_start_id INTEGER);
INSERT INTO A VALUES (1,2);
CREATE TABLE b (connection_node_end_id INTEGER, connection_node_start_id INTEGER);
INSERT INTO B VALUES (100, 101);
SELECT * from d(array['a','b']);
c
-----
1
2
100
101
(4 rows)