Postgresql
動態查找兩個表之間不匹配的行
我這裡有一個函式,它應該將兩個表作為參數並檢查它們是否相同。
create or replace function testing.equal_tables( varchar, varchar) returns void as $$ begin execute 'select * from (select * from ' || $1 ||' except select * from ' || $2 || ') a union (select * from ' || $2 || ' except select * from ' || $1 || ');' ; end; $$ language plpgsql;
當我用這兩張表呼叫它時,一張有 20 行,一張有 10 行,我只得到空集,這不是正確的結果:
select testing.equal_tables('ee1', 'ee2');
當我修改函式以返回語句的字元串時,它正確返回,但這仍然無濟於事,因為我想使用函式、準備好的語句或其他東西來執行返回字元串。有什麼辦法可以使這個功能起作用嗎?
我從您的範例中得出,您只想匹配具有相同結構(兼容的行類型)的表。
基本查詢
首先,您的基本查詢不必要地複雜並且可能不正確。改為考慮:
(TABLE a EXCEPT ALL TABLE b) -- ALL keeps duplicate rows and is faster UNION ALL (TABLE b EXCEPT ALL TABLE a);
我懷疑你想折疊重複。通常情況下,表一開始就不包含完整的重複項,省略
ALL
關鍵字會指示 Postgres 在每個步驟中嘗試折疊重複項,這只是浪費時間。細節:即使完全重複的行是可能的,最好將它們全部返回以避免誤導性答案***。***如果你想在結果中折疊重複,一個單一
UNION
的工作:(TABLE a EXCEPT ALL TABLE b) UNION (TABLE b EXCEPT ALL TABLE a);
但是,查詢
NATURAL FULL OUTER JOIN
效率更高,幾乎相同:SELECT * FROM a NATURAL FULL OUTER JOIN b WHERE a IS NULL OR b IS NULL;
有兩個微妙的、奇異的極端案例:
- 這將返回所有以 NULL 開頭的行。不過,這將是一個奇怪的表格設計,允許這樣的行。
- 這不會返回與另一個表中相同數量的重複行不匹配的重複行:單個匹配足以消除所有 - 這與結果中的折疊重複略有不同!看看下面的小提琴。同樣,這將是一個奇怪的表格設計,允許完全重複的行。
功能
到目前為止,您所擁有的內容因多種原因而不起作用。
要動態返回實際的表行(而不僅僅是計數或文本表示),您需要使用多態類型。
由於第二個表必然具有兼容的行類型(根據我的假設),因此只需為此輸入表名就足夠了。
CREATE OR REPLACE FUNCTION f_tbl_diff(_tbl1_type ANYELEMENT, _tbl2 text) RETURNS SETOF ANYELEMENT AS $func$ BEGIN RETURN QUERY EXECUTE format(' SELECT * FROM %1$s NATURAL FULL OUTER JOIN %2$I WHERE %1$s IS NULL OR %2$I IS NULL' , pg_typeof(_tbl1_type), _tbl2); END $func$ LANGUAGE plpgsql;
稱呼:
SELECT * FROM f_tbl_diff(NULL::a, 'b');
注意第一個參數的特殊語法!我們送出定義返回類型的實際行,而不僅僅是表名。關於 SO 的相關答案有很多細節(滾動到最後一章):
這將返回另一個表中沒有完全匹配的所有行
SQL Fiddle還展示了奇異的極端情況。
關於動態 SQL: