从多个表中的多个列中获取 ID 作为一组或数组
Get IDs from multiple columns in multiple tables as one set or array
我有多个 tables,每两行感兴趣:connection_node_start_id
和 connection_node_end_id
。我的目标是获取所有这些 ID 的集合,可以是平面 ARRAY
或包含一行的新 TABLE
。
示例输出数组:
result = {1,4,7,9,2,5}
示例输出 TABLE:
IDS
-------
1
4
7
9
2
5
我的第一次尝试有些笨拙,并且无法正常工作,因为 SELECT
语句只有 returns 一行。看来一定有一个简单的方法可以做到这一点,有人能指出我正确的方向吗?
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
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';
测试table:
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
EXECUTE ... INTO
语句只能return单行数据:
If multiple rows are returned, only the first will be assigned to the INTO variable.
为了连接所有行的值,您必须先按列聚合它们,然后附加数组:
EXECUTE 'SELECT array_agg(connection_node_end_id) ||
array_agg(connection_node_start_id) FROM ' || table_name INTO sel;
对于 Postgres 9.3+
CREATE OR REPLACE FUNCTION get_connection_nodes(text[])
RETURNS TABLE (ids int) AS
$func$
DECLARE
_tbl text;
BEGIN
FOREACH _tbl IN ARRAY
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() tbl
);
END
$func$ LANGUAGE plpgsql;
相关:
- Loop through like tables in a schema
是随 Postgres 9.3.
引入的
对于较旧的 Postgres
您也可以在 SELECT 列表中使用设置返回函数 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() 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() tbl
);
END
$func$ LANGUAGE plpgsql;
format()
是在 pg 9.1 中引入的。
大 tables 可能会慢一点,因为每个 table 对每一列扫描一次(所以这里是 2 次)。结果中的排序顺序也不同 - 但这对您来说似乎无关紧要。
一定要清理转义标识符以防止 SQL 注入和其他非法语法。详情:
- Table name as a PostgreSQL function parameter
您可能正在寻找这样的东西:
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)
我有多个 tables,每两行感兴趣:connection_node_start_id
和 connection_node_end_id
。我的目标是获取所有这些 ID 的集合,可以是平面 ARRAY
或包含一行的新 TABLE
。
示例输出数组:
result = {1,4,7,9,2,5}
示例输出 TABLE:
IDS
-------
1
4
7
9
2
5
我的第一次尝试有些笨拙,并且无法正常工作,因为 SELECT
语句只有 returns 一行。看来一定有一个简单的方法可以做到这一点,有人能指出我正确的方向吗?
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
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';
测试table:
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
EXECUTE ... INTO
语句只能return单行数据:
If multiple rows are returned, only the first will be assigned to the INTO variable.
为了连接所有行的值,您必须先按列聚合它们,然后附加数组:
EXECUTE 'SELECT array_agg(connection_node_end_id) ||
array_agg(connection_node_start_id) FROM ' || table_name INTO sel;
对于 Postgres 9.3+
CREATE OR REPLACE FUNCTION get_connection_nodes(text[])
RETURNS TABLE (ids int) AS
$func$
DECLARE
_tbl text;
BEGIN
FOREACH _tbl IN ARRAY
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() tbl
);
END
$func$ LANGUAGE plpgsql;
相关:
- Loop through like tables in a schema
对于较旧的 Postgres
您也可以在 SELECT 列表中使用设置返回函数 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() 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() tbl
);
END
$func$ LANGUAGE plpgsql;
format()
是在 pg 9.1 中引入的。
大 tables 可能会慢一点,因为每个 table 对每一列扫描一次(所以这里是 2 次)。结果中的排序顺序也不同 - 但这对您来说似乎无关紧要。
一定要清理转义标识符以防止 SQL 注入和其他非法语法。详情:
- Table name as a PostgreSQL function parameter
您可能正在寻找这样的东西:
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)