PostgreSQL函数从动态表名返回结果集

时间:2016-05-31 16:17:05

标签: sql postgresql function plpgsql

在我的数据库中,我有标准的app表和备份表。例如。对于表“employee”,我有一个名为“bak_employee”的表。 bak_employee表是employee表的备份。我用它在测试之间恢复employee表。

我想我可以使用这些“bak_”表来查看测试期间发生的变化,如下所示:

SELECT * FROM employee EXCEPT SELECT * FROM bak_employee

这将显示插入和更新的记录。我现在暂时忽略删除的记录。

现在,我想要做的是遍历我的数据库中的所有表,看看是否有任何表中的任何更改。我正在考虑将此作为一个功能,因此一次又一次地调用很容易。这就是我到目前为止所做的:

CREATE OR REPLACE FUNCTION public.show_diff()
  RETURNS SETOF diff_tables AS
$BODY$

DECLARE
  app_tables text;
BEGIN

    FOR app_tables IN
        SELECT table_name  

        FROM   information_schema.tables 

        WHERE  table_catalog = 'myDatabase' 
          AND  table_schema = 'public'
          AND  table_name not like 'bak_%'          -- exclude existing backup tables
    LOOP

        -- somehow loop through tables to see what's changed something like:
        EXECUTE 'SELECT * FROM ' || app_tables || ' EXCEPT SELECT * FROM bak_' || app_tables;

    END LOOP;

    RETURN;
END;
$BODY$
LANGUAGE plpgsql;

但显然这不会给我任何有用的信息。任何帮助将不胜感激。

1 个答案:

答案 0 :(得分:3)

您无法在同一个调用中从同一函数返回各种众所周知的行类型。一个便宜的修复是将每一行类型转换为text,因此我们有一个共同的返回类型。

CREATE OR REPLACE FUNCTION public.show_diff()
  RETURNS SETOF text AS   -- text!!
$func$
DECLARE
  app_table text;
BEGIN
   FOR app_table IN
      SELECT table_name  
      FROM   information_schema.tables 
      WHERE  table_catalog = 'myDatabase' 
      AND    table_schema = 'public'
      AND    table_name NOT LIKE 'bak_%'   -- exclude existing backup tables
   LOOP
      RETURN NEXT ' ';
      RETURN NEXT '=== ' || app_table || ' ===';
      RETURN QUERY EXECUTE format(
        'SELECT x::text FROM (TABLE %I EXCEPT ALL TABLE %I) x'
       , app_table, 'bak_' || app_table);
   END LOOP;

   RETURN;
END
$func$ LANGUAGE plpgsql;

呼叫:

SELECT * FROM public.show_diff();

我最初接受了测试suggested by @a_horse,但在your comment后我意识到没有必要这样做。 EXCEPT认为NULL相等并显示所有差异。

在此期间,我进一步改进并简化了您的解决方案。使用EXCEPT ALL:更便宜,不会有折叠完整重复的风险。

TABLE只是语法糖。

但是,如果你有一个唯一(组合)列的索引,我之前建议的JOIN应该更快:找到唯一可能重复的索引应该大大便宜。

关键元素是将行类型转换为text x::text )。

您甚至可以使该函数适用于任何表 - 但一次不能超过一个:使用多态参数类型: