将 Postgres 中的外键定义为目标的子集 table
Define foreign key in Postgres to a subset of a target table
示例:
我有:
Table A:
int id
int table_b_id
Table B:
int id
text type
我想在列 table_b_id 上添加约束检查,以验证它仅指向 table B 中类型值为 'X'.[=16= 的行]
我无法更改 table 结构。
我知道它可以用 'CHECK' 和一个 postgres 函数来完成特定的查询,但我看到有人建议避免它。
任何有关实施它的最佳方法的意见都会有所帮助。
您指的不是 FOREIGN KEY
,在 PostgreSQL 中,它指的是另一个 table 中的(多个)列,其中有一个唯一索引that/those 列,并且当 that/those 列的值更改(ON UPDATE
、ON DELETE
)时可能具有关联的自动操作。
您正在尝试实施一种特定类型的参照完整性,类似于 FOREIGN KEY
所做的。你可以用一个CHECK
子句和一个函数来实现(因为CHECK
子句不允许子查询),你也可以用table inheritance and range partitioning来实现(指的是子table 仅包含 type = 'X'
) 的行,但使用触发器执行此操作可能是最简单的:
CREATE FUNCTION trf_test_type_x() RETURNS trigger AS $$
BEGIN
PERFORM * FROM tableB WHERE id = NEW.table_b_id AND type = 'X';
IF NOT FOUND THEN
-- RAISE NOTICE 'Foreign key violation...';
RETURN NULL;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE tr_test_type_x
BEFORE INSERT OR UPDATE ON tableA
FOR EACH ROW EXECUTE PROCEDURE trf_test_type_x();
您可以在 tableB
上创建部分索引以加快速度:
CREATE UNIQUE INDEX idx_type_X ON tableB(id) WHERE type = 'X';
在我看来,最优雅的解决方案是使用 inheritance 来获得子类型化行为:
PostgreSQL 9.3 Schema Setup with inheritance:
create table B ( id int primary key );
-- Instead to create a 'type' field, inherit from B for
-- each type with custom properties:
create table B_X ( -- some_data varchar(10 ),
constraint pk primary key (id)
) inherits (B);
-- Sample data:
insert into B_X (id) values ( 1 );
insert into B (id) values ( 2 );
-- Now, instead to reference B, you should reference B_X:
create table A ( id int primary key, B_id int references B_X(id) );
-- Here it is:
insert into A values ( 1, 1 );
--Inserting wrong values will causes violation:
insert into A values ( 2, 2 );
ERROR: insert or update on table "a" violates foreign key constraint "a_b_id_fkey"
Detail: Key (b_id)=(2) is not present in table "b_x".
正在从库中检索所有数据 table:
select * from B
| id |
|----|
| 2 |
| 1 |
检索类型为的数据:
SELECT p.relname, c.*
FROM B c inner join pg_class p on c.tableoid = p.oid
| relname | id |
|---------|----|
| b | 2 |
| b_x | 1 |
示例:
我有:
Table A:
int id
int table_b_id
Table B:
int id
text type
我想在列 table_b_id 上添加约束检查,以验证它仅指向 table B 中类型值为 'X'.[=16= 的行]
我无法更改 table 结构。
我知道它可以用 'CHECK' 和一个 postgres 函数来完成特定的查询,但我看到有人建议避免它。
任何有关实施它的最佳方法的意见都会有所帮助。
您指的不是 FOREIGN KEY
,在 PostgreSQL 中,它指的是另一个 table 中的(多个)列,其中有一个唯一索引that/those 列,并且当 that/those 列的值更改(ON UPDATE
、ON DELETE
)时可能具有关联的自动操作。
您正在尝试实施一种特定类型的参照完整性,类似于 FOREIGN KEY
所做的。你可以用一个CHECK
子句和一个函数来实现(因为CHECK
子句不允许子查询),你也可以用table inheritance and range partitioning来实现(指的是子table 仅包含 type = 'X'
) 的行,但使用触发器执行此操作可能是最简单的:
CREATE FUNCTION trf_test_type_x() RETURNS trigger AS $$
BEGIN
PERFORM * FROM tableB WHERE id = NEW.table_b_id AND type = 'X';
IF NOT FOUND THEN
-- RAISE NOTICE 'Foreign key violation...';
RETURN NULL;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE tr_test_type_x
BEFORE INSERT OR UPDATE ON tableA
FOR EACH ROW EXECUTE PROCEDURE trf_test_type_x();
您可以在 tableB
上创建部分索引以加快速度:
CREATE UNIQUE INDEX idx_type_X ON tableB(id) WHERE type = 'X';
在我看来,最优雅的解决方案是使用 inheritance 来获得子类型化行为:
PostgreSQL 9.3 Schema Setup with inheritance:
create table B ( id int primary key );
-- Instead to create a 'type' field, inherit from B for
-- each type with custom properties:
create table B_X ( -- some_data varchar(10 ),
constraint pk primary key (id)
) inherits (B);
-- Sample data:
insert into B_X (id) values ( 1 );
insert into B (id) values ( 2 );
-- Now, instead to reference B, you should reference B_X:
create table A ( id int primary key, B_id int references B_X(id) );
-- Here it is:
insert into A values ( 1, 1 );
--Inserting wrong values will causes violation:
insert into A values ( 2, 2 );
ERROR: insert or update on table "a" violates foreign key constraint "a_b_id_fkey" Detail: Key (b_id)=(2) is not present in table "b_x".
正在从库中检索所有数据 table:
select * from B
| id |
|----|
| 2 |
| 1 |
检索类型为的数据:
SELECT p.relname, c.*
FROM B c inner join pg_class p on c.tableoid = p.oid
| relname | id |
|---------|----|
| b | 2 |
| b_x | 1 |