postgresql 从多个表中的多个列中获取ID作为一个集合或数组

5ssjco0h  于 2023-06-22  发布在  PostgreSQL
关注(0)|答案(3)|浏览(192)

我有多个表,其中每两行都是感兴趣的:connection_node_start_idconnection_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
xvw2m8pv

xvw2m8pv1#

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$;

应该与pg 8.4+(或甚至更老)工作。同样适用于当前的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页中引入。

对于大表可能会慢一点,因为每个表对每个列扫描一次(这里是2次)。结果中的排序顺序也不同-但这对您来说似乎并不重要。
请务必清理转义标识符,以防止SQL注入和其他非法语法。详细内容:

  • 表名作为PostgreSQL函数参数
wooyq4lh

wooyq4lh2#

EXECUTE ... INTO语句只能返回单行中的数据:
如果返回多行,则只有第一行将被分配给INTO变量。
为了连接所有行的值,你必须先按列聚合它们,然后追加数组:

EXECUTE 'SELECT array_agg(connection_node_end_id) || 
                array_agg(connection_node_start_id) FROM ' || table_name INTO sel;
jckbn6z7

jckbn6z73#

你可能正在寻找这样的东西:

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)

相关问题