2016-05-31 417 views
0

在我的數據庫中,我有標準的應用程序表和備份表。例如。對於「員工」表,我有一個名爲「bak_employee」的表。 bak_employee表是員工表的備份。我用它在測試之間恢復員工表。PostgreSQL函數返回來自動態表名稱的結果集

我想我可以用這些「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

無關,而是:在'except'不會顯示你所有的差異。例如'select * from employee e full outer join bak_employee b on e.id = b.id where e is distinct from b' would be better。 –

+0

這不能用「通用」函數完成,因爲該函數只能返回一個「事物」。但是這些表格將具有不同數量的列,並且結果集需要具有用於**所有**行的相同數量的列。可以工作的是將每個行作爲JSON文檔或包含修改列的'hstore'作爲關鍵字返回。 –

回答

2

在同一個調用中,不能返回來自同一函數的各種衆所周知的行類型。一個便宜的修正是將每一行類型轉換爲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只是語法糖。

然而,如果你有一個獨特的(組合)的索引列(S),一個JOIN像我建議之前,應該會更快:找到通過的唯一可能重複指數應該大幅降低。

重要元素是鑄造行類型爲textx::text)。

你甚至可以使任何表的功能工作 - 但從來沒有一個超過一次:使用多態參數類型:

+0

真棒!!!!愛你的解決方案。除了我改變了一下select語句:SELECT x :: text FROM(SELECT * FROM%I EXCEPT SELECT * FROM%I)as x – Steven

+0

@Steven:你說的對,'EXCEPT'應該可以正常工作。或者更好,但是:'除了所有'。不過,加入*可能會更快。考慮更新。 –