Фильтры строк
Эта страница переведена при помощи нейросети GigaChat.
По умолчанию все данные из всех опубликованных таблиц будут реплицированы соответствующим подписчикам. Реплицируемые данные могут быть уменьшены за счет использования фильтра строк. Пользователь может выбрать использование фильтров строк по причинам поведения, безопасности или производительности. Если опубликованная таблица устанавливает фильтр строки, строка реплицируется только в том случае, если ее данные удовлетворяют выражению фильтра строки. Это позволяет частично реплицировать набор таблиц. Фильтр строки определяется для каждой таблицы. Используйте предложение WHERE
после имени каждой публикуемой таблицы, для которой требуется фильтрация данных. Предложение WHERE
должно быть заключено в скобки. См. подробности в разделе CREATE PUBLICATION.
Правила фильтрации строк
Фильтры строк применяются до публикации изменений. Если фильтр строки оценивается как false
или NULL
, то строка не реплицируется. Выражение условия WHERE
вычисляется с той же ролью, которая используется для подключения к репликации (т.е. роль, указанная в условии CONNECTION
команды CREATE SUBSCRIPTION). Фильтры строк не имеют никакого эффекта для команды TRUNCATE
.
Ограничения выражений
Предложение WHERE
допускает только простые выражения. Оно не может содержать функции, операторы, типы и сопоставления, определенные пользователем, ссылки на системные столбцы или встроенные функции, которые не являются неизменными.
Если публикация публикует операции UPDATE
или DELETE
, то условие фильтра строки WHERE
должно содержать только столбцы, которые охвачены идентичностью реплики (см. REPLICA IDENTITY
). Если публикация публикует только операции INSERT
, условие фильтра строки WHERE
может использовать любой столбец.
Преобразования UPDATE
Всякий раз, когда обрабатывается UPDATE
, выражение фильтра строки оценивается как для старой, так и для новой строки (т.е. с использованием данных до и после обновления). Если обе оценки являются true
, он реплицирует изменение UPDATE
. Если обе оценки равны false
, изменение не реплицируется. Если только одна из старых/новых строк соответствует выражению фильтра строки, UPDATE
преобразуется в INSERT
или DELETE
, чтобы избежать каких-либо несоответствий данных. Строка на подписчике должна отражать то, что определено выражением фильтра строки на издателя.
Если старая строка удовлетворяет условию фильтра строки (она была отправлена подписчику), но новая строка нет, то, с точки зрения согласованности данных, старую строку следует удалить из подписчика. Таким образом, UPDATE
преобразуется в DELETE
.
Если старая строка не удовлетворяет выражению фильтра строки (она не была отправлена подписчику), но новая строка удовлетворяет, то с точки зрения согласованности данных новая строка должна быть добавлена к подписчику. Таким образом, UPDATE
преобразуется в INSERT
.
Таблица ниже суммирует применяемые преобразования.
Обзор преобразования UPDATE
Старая строка | Новая строка | Преобразование |
---|---|---|
нет совпадения | нет совпадения | не реплицировать |
нет совпадения | совпадение | INSERT |
совпадение | несоответствие | DELETE |
совпадение | сопоставление | UPDATE |
Партиционированные таблицы
Если публикация содержит разделенную таблицу, параметр публикации publish_via_partition_root
определяет, какой фильтр строки используется. Если publish_via_partition_root
равен true
, то используется фильтр строки корневой партиционированной таблицы. В противном случае, если publish_via_partition_root
равно false
(по умолчанию), используются фильтры строк каждой партиции.
Начальная синхронизация данных
Если подписка требует копирования существующих данных таблицы и публикация содержит WHERE
предложения, то только данные, удовлетворяющие выражениям фильтра строки, копируются подписчику.
Если у подписки есть несколько публикаций, в которых таблица была опубликована с разными WHERE
предложениями, будут скопированы строки, которые удовлетворяют любому из этих выражений. См. раздел ниже для получения подробной информации.
Поскольку начальная синхронизация данных не учитывает параметр publish
при копировании существующих данных таблицы, могут быть скопированы некоторые строки, которые не будут реплицироваться с использованием DML. Смотрите раздел «Начальный снимок» и примеры в разделе «Примеры».
Если на стороне подписчика используется версия ниже PostgreSQL 15, то копирование существующих данных не использует фильтры строк, даже если они определены в публикации. Это связано с тем, что старые выпуски могут копировать только все данные таблицы.
Комбинирование нескольких фильтров строк
Если подписка имеет несколько публикаций, в которых одна и та же таблица была опубликована с разными фильтрами строк (для одной и той же операции publish
), эти выражения объединяются оператором OR
, так что строки, удовлетворяющие любому из выражений, будут реплицированы. Это означает, что все остальные фильтры строк для этой же таблицы становятся избыточными, если:
- Одна из публикаций не содержит фильтра строк.
- Одна из публикаций была создана с использованием
FOR ALL TABLES
. Этот пункт не допускает фильтров строк. - Одна из публикаций была создана с использованием
FOR TABLES IN SCHEMA
, и таблица принадлежит указанной схеме. Этот пункт также не допускает фильтров строк.
Примеры
Создайте несколько таблиц для использования в следующих примерах:
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
Вставьте несколько строк. Только строки, удовлетворяющие условию t1 WHERE
публикации p1
, реплицируются:
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)
Обновите некоторые данные, где старые и новые значения строки оба удовлетворяют условию t1 WHERE
публикации p1
. 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)
Обновите некоторые данные, где старые значения строки не удовлетворяли условию t1 WHERE
публикации p1
, но новые значения строки удовлетворяют ему. 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)
Обновите некоторые данные, где старые значения строки удовлетворяли условию t1 WHERE
публикации p1
, но новые значения строки этому условию не соответствуют. 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
установлен как истинный. Фильтры строк определены как для таблицы с разбиением на разделы (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
Вставьте некоторые значения непосредственно в таблицы parent
и child
. Они реплицируются с использованием фильтра строки parent
(потому что publish_via_partition_root
истинно):
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
установлен как ложный. Фильтр строк определен на разделе (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
ложно):
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)