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

标签 postgresql function plpgsql dynamic-sql multiple-tables

我有多个表,每两行都感兴趣:connection_node_start_idconnection_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/

相关文章:

c# - 从流读取时出现 Npgsql 异常,Postgres

C++ 嵌套构造函数调用与函数声明

c - C语言如何将字符串从尾部复制到开头

sql - 使用 JSON 参数的 Postgres 批量插入函数

sql - RoR model.where() 使用 pg 数组类型

sql - 将两个单独的 SQL 查询的结果与 'OR' 合并

sql - ST_SnapToGrid 单位

azure - HTTP 401 Azure Terraform 操作组向 Azure 函数发送日志搜索警报

postgresql - 在 postgres plpgsql 函数中重用 json 解析的输入

postgresql - SELECT 在 PL/pgSQL 函数中引发异常