在我的数据库中,我有标准的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;
但显然这不会给我任何有用的信息。任何帮助将不胜感激。
答案 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
)。
您甚至可以使该函数适用于任何表 - 但一次不能超过一个:使用多态参数类型: