将 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 UPDATEON 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

Results:

| id |
|----|
|  2 |
|  1 |

检索类型为的数据:

SELECT p.relname, c.*
FROM B c inner join pg_class p on c.tableoid = p.oid

Results:

| relname | id |
|---------|----|
|       b |  2 |
|     b_x |  1 |