支援的版本:目前版本 (17) / 16 / 15
開發版本:devel

29.4. 列篩選器 #

預設情況下,所有已發布表格的所有資料都會複寫到適當的訂閱者。複寫的資料可以使用列篩選器來減少。使用者可能會因為行為、安全性或效能原因而選擇使用列篩選器。如果已發布的表格設定了列篩選器,則只有當其資料滿足列篩選器運算式時,才會複寫列。這允許部分複寫一組表格。列篩選器是針對每個表格定義的。在表格名稱之後使用 WHERE 子句,針對每個需要篩選掉資料的已發布表格。WHERE 子句必須用括號括起來。有關詳細資訊,請參閱CREATE PUBLICATION

29.4.1. 列篩選器規則 #

列篩選器會在發布變更之前套用。如果列篩選器的評估結果為 falseNULL,則不會複寫該列。WHERE 子句運算式會使用與複寫連線相同的角色進行評估 (即,CREATE SUBSCRIPTIONCONNECTION 子句中指定的角色)。列篩選器對 TRUNCATE 命令沒有作用。

29.4.2. 運算式限制 #

WHERE 子句僅允許簡單運算式。它不能包含使用者定義的函式、運算子、類型和定序、系統欄位參考或非不可變的內建函式。

如果發布項目發布 UPDATEDELETE 操作,則列篩選器 WHERE 子句必須僅包含 replica identity 所涵蓋的欄位 (請參閱 REPLICA IDENTITY)。如果發布項目僅發布 INSERT 操作,則列篩選器 WHERE 子句可以使用任何欄位。

29.4.3. UPDATE 轉換 #

每當處理 UPDATE 時,都會針對舊列和新列評估列篩選器運算式 (即,使用更新前後的資料)。如果兩個評估結果都為 true,則會複寫 UPDATE 變更。如果兩個評估結果都為 false,則不會複寫變更。如果只有一個舊/新列符合列篩選器運算式,則 UPDATE 會轉換為 INSERTDELETE,以避免任何資料不一致。訂閱者上的列應反映發布者上列篩選器運算式定義的內容。

如果舊列滿足列篩選器運算式 (已傳送到訂閱者),但新列不滿足,那麼從資料一致性的角度來看,應該從訂閱者中移除舊列。因此,UPDATE 會轉換為 DELETE

如果舊列不滿足列篩選器運算式 (未傳送到訂閱者),但新列滿足,那麼從資料一致性的角度來看,應該將新列新增到訂閱者。因此,UPDATE 會轉換為 INSERT

表 29.1 總結了套用的轉換。

表 29.1. UPDATE 轉換摘要

舊列 新列 轉換
不符合 不符合 不複寫
不符合 符合 INSERT
符合 不符合 DELETE
符合 符合 UPDATE

29.4.4. 分割表 #

如果發布項目包含分割表,則發布項目參數 publish_via_partition_root 決定使用哪個列篩選器。如果 publish_via_partition_roottrue,則使用根分割表的列篩選器。否則,如果 publish_via_partition_rootfalse (預設值),則使用每個分割區的列篩選器。

29.4.5. 初始資料同步 #

如果訂閱需要複製先前存在的表格資料,並且發布項目包含 WHERE 子句,則只會將滿足列篩選器運算式的資料複製到訂閱者。

如果訂閱中有多個發布項目,其中一個表格已使用不同的 WHERE 子句發布,則會複製滿足任何運算式的列。有關詳細資訊,請參閱第 29.4.6 節

警告

由於初始資料同步在複製現有表格資料時不考慮 publish 參數,因此可能會複製某些不會使用 DML 複寫的列。請參閱第 29.8.1 節,並參閱第 29.2.2 節 以取得範例。

注意

如果訂閱者是 15 之前的版本,即使在發布項目中定義了列篩選器,複製先前存在的資料也不會使用列篩選器。這是因為舊版本只能複製整個表格資料。

29.4.6. 組合多個列篩選器 #

如果訂閱包含多個發布,其中相同的資料表已使用不同的列篩選器發布(對於相同的 publish 操作),則這些表達式會進行 OR 運算,以便滿足 任何 表達式的列將被複製。這表示如果符合以下情況,則相同資料表的所有其他列篩選器都會變得多餘:

  • 其中一個發布沒有列篩選器。

  • 其中一個發布是使用 FOR ALL TABLES 建立的。此子句不允許列篩選器。

  • 其中一個發布是使用 FOR TABLES IN SCHEMA 建立的,且資料表屬於所參照的 schema。此子句不允許列篩選器。

29.4.7. 範例 #

建立一些資料表以用於以下範例。

test_pub=# CREATE TABLE t1(a int, b int, c text, PRIMARY KEY(a,c));
CREATE TABLE
test_pub=# CREATE TABLE t2(d int, e int, f int, PRIMARY KEY(d));
CREATE TABLE
test_pub=# CREATE TABLE t3(g int, h int, i int, PRIMARY KEY(g));
CREATE TABLE

建立一些發布。發布 p1 有一個資料表 (t1),且該資料表具有列篩選器。發布 p2 有兩個資料表。資料表 t1 沒有列篩選器,而資料表 t2 則有列篩選器。發布 p3 有兩個資料表,且它們都具有列篩選器。

test_pub=# CREATE PUBLICATION p1 FOR TABLE t1 WHERE (a > 5 AND c = 'NSW');
CREATE PUBLICATION
test_pub=# CREATE PUBLICATION p2 FOR TABLE t1, t2 WHERE (e = 99);
CREATE PUBLICATION
test_pub=# CREATE PUBLICATION p3 FOR TABLE t2 WHERE (d = 10), t3 WHERE (g = 10);
CREATE PUBLICATION

psql 可用於顯示每個發布的列篩選器表達式(如果已定義)。

test_pub=# \dRp+
                               Publication p1
  Owner   | All tables | Inserts | Updates | Deletes | Truncates | Via root
----------+------------+---------+---------+---------+-----------+----------
 postgres | f          | t       | t       | t       | t         | f
Tables:
    "public.t1" WHERE ((a > 5) AND (c = 'NSW'::text))

                               Publication p2
  Owner   | All tables | Inserts | Updates | Deletes | Truncates | Via root
----------+------------+---------+---------+---------+-----------+----------
 postgres | f          | t       | t       | t       | t         | f
Tables:
    "public.t1"
    "public.t2" WHERE (e = 99)

                               Publication p3
  Owner   | All tables | Inserts | Updates | Deletes | Truncates | Via root
----------+------------+---------+---------+---------+-----------+----------
 postgres | f          | t       | t       | t       | t         | f
Tables:
    "public.t2" WHERE (d = 10)
    "public.t3" WHERE (g = 10)

psql 可用於顯示每個資料表的列篩選器表達式(如果已定義)。請注意,資料表 t1 是兩個發布的成員,但僅在 p1 中具有列篩選器。請注意,資料表 t2 是兩個發布的成員,且每個發布中都有不同的列篩選器。

test_pub=# \d t1
                 Table "public.t1"
 Column |  Type   | Collation | Nullable | Default
--------+---------+-----------+----------+---------
 a      | integer |           | not null |
 b      | integer |           |          |
 c      | text    |           | not null |
Indexes:
    "t1_pkey" PRIMARY KEY, btree (a, c)
Publications:
    "p1" WHERE ((a > 5) AND (c = 'NSW'::text))
    "p2"

test_pub=# \d t2
                 Table "public.t2"
 Column |  Type   | Collation | Nullable | Default
--------+---------+-----------+----------+---------
 d      | integer |           | not null |
 e      | integer |           |          |
 f      | integer |           |          |
Indexes:
    "t2_pkey" PRIMARY KEY, btree (d)
Publications:
    "p2" WHERE (e = 99)
    "p3" WHERE (d = 10)

test_pub=# \d t3
                 Table "public.t3"
 Column |  Type   | Collation | Nullable | Default
--------+---------+-----------+----------+---------
 g      | integer |           | not null |
 h      | integer |           |          |
 i      | integer |           |          |
Indexes:
    "t3_pkey" PRIMARY KEY, btree (g)
Publications:
    "p3" WHERE (g = 10)

在訂閱者節點上,建立一個與發布者上定義相同的資料表 t1,並建立訂閱 s1,該訂閱會訂閱發布 p1

test_sub=# CREATE TABLE t1(a int, b int, c text, PRIMARY KEY(a,c));
CREATE TABLE
test_sub=# CREATE SUBSCRIPTION s1
test_sub-# CONNECTION 'host=localhost dbname=test_pub application_name=s1'
test_sub-# PUBLICATION p1;
CREATE SUBSCRIPTION

插入一些列。僅複製滿足發布 p1t1 WHERE 子句的列。

test_pub=# INSERT INTO t1 VALUES (2, 102, 'NSW');
INSERT 0 1
test_pub=# INSERT INTO t1 VALUES (3, 103, 'QLD');
INSERT 0 1
test_pub=# INSERT INTO t1 VALUES (4, 104, 'VIC');
INSERT 0 1
test_pub=# INSERT INTO t1 VALUES (5, 105, 'ACT');
INSERT 0 1
test_pub=# INSERT INTO t1 VALUES (6, 106, 'NSW');
INSERT 0 1
test_pub=# INSERT INTO t1 VALUES (7, 107, 'NT');
INSERT 0 1
test_pub=# INSERT INTO t1 VALUES (8, 108, 'QLD');
INSERT 0 1
test_pub=# INSERT INTO t1 VALUES (9, 109, 'NSW');
INSERT 0 1

test_pub=# SELECT * FROM t1;
 a |  b  |  c
---+-----+-----
 2 | 102 | NSW
 3 | 103 | QLD
 4 | 104 | VIC
 5 | 105 | ACT
 6 | 106 | NSW
 7 | 107 | NT
 8 | 108 | QLD
 9 | 109 | NSW
(8 rows)
test_sub=# SELECT * FROM t1;
 a |  b  |  c
---+-----+-----
 6 | 106 | NSW
 9 | 109 | NSW
(2 rows)

更新一些資料,其中舊的和新的列值都滿足發布 p1t1 WHERE 子句。UPDATE 會像往常一樣複製變更。

test_pub=# UPDATE t1 SET b = 999 WHERE a = 6;
UPDATE 1

test_pub=# SELECT * FROM t1;
 a |  b  |  c
---+-----+-----
 2 | 102 | NSW
 3 | 103 | QLD
 4 | 104 | VIC
 5 | 105 | ACT
 7 | 107 | NT
 8 | 108 | QLD
 9 | 109 | NSW
 6 | 999 | NSW
(8 rows)
test_sub=# SELECT * FROM t1;
 a |  b  |  c
---+-----+-----
 9 | 109 | NSW
 6 | 999 | NSW
(2 rows)

更新一些資料,其中舊的列值不滿足發布 p1t1 WHERE 子句,但新的列值滿足它。UPDATE 會轉換為 INSERT,並且會複製變更。請參閱訂閱者上的新列。

test_pub=# UPDATE t1 SET a = 555 WHERE a = 2;
UPDATE 1

test_pub=# SELECT * FROM t1;
  a  |  b  |  c
-----+-----+-----
   3 | 103 | QLD
   4 | 104 | VIC
   5 | 105 | ACT
   7 | 107 | NT
   8 | 108 | QLD
   9 | 109 | NSW
   6 | 999 | NSW
 555 | 102 | NSW
(8 rows)
test_sub=# SELECT * FROM t1;
  a  |  b  |  c
-----+-----+-----
   9 | 109 | NSW
   6 | 999 | NSW
 555 | 102 | NSW
(3 rows)

更新一些資料,其中舊的列值滿足發布 p1t1 WHERE 子句,但新的列值不滿足它。UPDATE 會轉換為 DELETE,並且會複製變更。請參閱該列已從訂閱者中移除。

test_pub=# UPDATE t1 SET c = 'VIC' WHERE a = 9;
UPDATE 1

test_pub=# SELECT * FROM t1;
  a  |  b  |  c
-----+-----+-----
   3 | 103 | QLD
   4 | 104 | VIC
   5 | 105 | ACT
   7 | 107 | NT
   8 | 108 | QLD
   6 | 999 | NSW
 555 | 102 | NSW
   9 | 109 | VIC
(8 rows)
test_sub=# SELECT * FROM t1;
  a  |  b  |  c
-----+-----+-----
   6 | 999 | NSW
 555 | 102 | NSW
(2 rows)

以下範例顯示了在分割資料表的情況下,發布參數 publish_via_partition_root 如何決定將使用父資料表還是子資料表的列篩選器。

在發布者上建立一個分割資料表。

test_pub=# CREATE TABLE parent(a int PRIMARY KEY) PARTITION BY RANGE(a);
CREATE TABLE
test_pub=# CREATE TABLE child PARTITION OF parent DEFAULT;
CREATE TABLE

在訂閱者上建立相同的資料表。

test_sub=# CREATE TABLE parent(a int PRIMARY KEY) PARTITION BY RANGE(a);
CREATE TABLE
test_sub=# CREATE TABLE child PARTITION OF parent DEFAULT;
CREATE TABLE

建立一個發布 p4,然後訂閱它。發布參數 publish_via_partition_root 設為 true。在分割資料表 (parent) 和分割區 (child) 上都定義了列篩選器。

test_pub=# CREATE PUBLICATION p4 FOR TABLE parent WHERE (a < 5), child WHERE (a >= 5)
test_pub-# WITH (publish_via_partition_root=true);
CREATE PUBLICATION
test_sub=# CREATE SUBSCRIPTION s4
test_sub-# CONNECTION 'host=localhost dbname=test_pub application_name=s4'
test_sub-# PUBLICATION p4;
CREATE SUBSCRIPTION

直接將一些值插入 parentchild 資料表中。它們使用 parent 的列篩選器進行複製(因為 publish_via_partition_root 為 true)。

test_pub=# INSERT INTO parent VALUES (2), (4), (6);
INSERT 0 3
test_pub=# INSERT INTO child VALUES (3), (5), (7);
INSERT 0 3

test_pub=# SELECT * FROM parent ORDER BY a;
 a
---
 2
 3
 4
 5
 6
 7
(6 rows)
test_sub=# SELECT * FROM parent ORDER BY a;
 a
---
 2
 3
 4
(3 rows)

重複相同的測試,但 publish_via_partition_root 的值不同。發布參數 publish_via_partition_root 設為 false。在分割區 (child) 上定義了列篩選器。

test_pub=# DROP PUBLICATION p4;
DROP PUBLICATION
test_pub=# CREATE PUBLICATION p4 FOR TABLE parent, child WHERE (a >= 5)
test_pub-# WITH (publish_via_partition_root=false);
CREATE PUBLICATION
test_sub=# ALTER SUBSCRIPTION s4 REFRESH PUBLICATION;
ALTER SUBSCRIPTION

像之前一樣在發布者上執行插入操作。它們使用 child 的列篩選器進行複製(因為 publish_via_partition_root 為 false)。

test_pub=# TRUNCATE parent;
TRUNCATE TABLE
test_pub=# INSERT INTO parent VALUES (2), (4), (6);
INSERT 0 3
test_pub=# INSERT INTO child VALUES (3), (5), (7);
INSERT 0 3

test_pub=# SELECT * FROM parent ORDER BY a;
 a
---
 2
 3
 4
 5
 6
 7
(6 rows)
test_sub=# SELECT * FROM child ORDER BY a;
 a
---
 5
 6
 7
(3 rows)

提交更正

如果您在文件中發現任何不正確、與您使用特定功能的經驗不符或需要進一步澄清的地方,請使用此表單來報告文件問題。