REFERENCES 两个不同表中的两个不同列
REFERENCES two different columns in two different tables
在 Postgresql 数据库中,我们有一个 table 如下所示:
=# SELECT * FROM toy_cars;
serial_no | name
------------+---------------
199ER276FN | Snow Doctor
8BE0F79A3R | Flatbed Truck
D76185CE8G | Sand Speeder
=# SELECT * FROM toy_trains;
serial_no | name
-----------------+-------------
BMXH5R4T8K7KELD | Howler T140
B1Q1JJDQW9LQN0G | Quakester
8HO9240TO6RNNQ9 | Medusa 90
=# SELECT * FROM items_for_sale;
serial_no | in_stock
-----------------+---------------
199ER276FN | t
BMXH5R4T8K7KELD | t
B1Q1JJDQW9LQN0G | f
8BE0F79A3R | f
8HO9240TO6RNNQ9 | t
D76185CE8G | f
注:
每个 serial_no
列都是 table 的主键,in_stock
是一个布尔值。
serial_no
在 toy_cars
table 中有一个正则表达式 CHECK
限制只允许 10 个字符。
serial_no
在 toy_trains
table 中有一个正则表达式 CHECK
限制只允许 15 个字符。
serial_no
in the items_for_sale
table 是玩具车或火车的序列号,并且有一个正则表达式 CHECK
限制允许仅限 10 或 15 个字符。
所有 serial_no
列都有 UNIQUE
约束。
我们想在 items_for_sale
table 中对 serial_no
添加 REFERENCES
检查,以确保输入的序列号存在于 toy_cars
table 或 toy_trains
table.
因此,如果我要尝试 INSERT INTO items_for_sale VALUES('KYVGK0DBYXPMWW8','f');
这将失败,因为该序列号不存在于 toy_cars
或 toy_trains
.
中
如何做到这一点?我们更喜欢使用一个 table(就像现在的结构一样)。
这里的问题是您想要检查两个(其他)table 中是否存在一个键,并且您不能在单个列上使用外键约束来强制执行该操作。 Postgres 允许您为自定义检查创建 CHECK
表达式,但作为 the manual says:
PostgreSQL does not support CHECK constraints that reference table
data other than the new or updated row being checked. (...) If
possible, use UNIQUE, EXCLUDE, or FOREIGN KEY constraints to express
cross-row and cross-table restrictions.
If what you desire is a one-time check against other rows at row
insertion, rather than a continuously-maintained consistency
guarantee, a custom trigger can be used to implement that.
请参阅 this very related question 了解不同的解决方案。
某些数据库(MS SQL 服务器)允许您use a function in CHECK
expressions。那将是最佳选择,但 Postgres 不允许该语法。
对于 PostgreSQL,您需要创建一个 TRIGGER
,它将在某些内容插入 items_for_sale
时执行。另一方面,确实允许函数或交叉table检查。
看起来像这样:
CREATE TRIGGER check_serial_present
BEFORE INSERT ON items_for_sale
FOR EACH ROW
EXECUTE FUNCTION assert_presence_of_serial(); -- implement this function
另一个链接的问题也提到了一种无需触发器即可实现此目的的非常优雅的方法:
A clean solution without triggers: add redundant columns and include them in FOREIGN KEY constraints
我非常喜欢它,因为它在概念上非常简单且易于掌握。我认为它只涉及这些步骤:
ALTER TABLE
添加两列:toy_car_serial
和 toy_trains_serial
,均为 NULLABLE,但对上述 table 具有 FOREIGN KEY
约束。
- 确保任何
INSERT
都会将序列号插入 serial_no
和 toy_car_serial
或 serial_no
和 toy_car_serial
.
- 添加
CHECK( toy_car_serial = serial_no OR toy_trains_serial = serial_no)
.
我认为两个冗余行和对您的插入内容的轻微修改比替代方法要少得多。
在 Postgresql 数据库中,我们有一个 table 如下所示:
=# SELECT * FROM toy_cars;
serial_no | name
------------+---------------
199ER276FN | Snow Doctor
8BE0F79A3R | Flatbed Truck
D76185CE8G | Sand Speeder
=# SELECT * FROM toy_trains;
serial_no | name
-----------------+-------------
BMXH5R4T8K7KELD | Howler T140
B1Q1JJDQW9LQN0G | Quakester
8HO9240TO6RNNQ9 | Medusa 90
=# SELECT * FROM items_for_sale;
serial_no | in_stock
-----------------+---------------
199ER276FN | t
BMXH5R4T8K7KELD | t
B1Q1JJDQW9LQN0G | f
8BE0F79A3R | f
8HO9240TO6RNNQ9 | t
D76185CE8G | f
注:
每个
serial_no
列都是 table 的主键,in_stock
是一个布尔值。serial_no
在toy_cars
table 中有一个正则表达式CHECK
限制只允许 10 个字符。serial_no
在toy_trains
table 中有一个正则表达式CHECK
限制只允许 15 个字符。serial_no
in theitems_for_sale
table 是玩具车或火车的序列号,并且有一个正则表达式CHECK
限制允许仅限 10 或 15 个字符。所有
serial_no
列都有UNIQUE
约束。
我们想在 items_for_sale
table 中对 serial_no
添加 REFERENCES
检查,以确保输入的序列号存在于 toy_cars
table 或 toy_trains
table.
因此,如果我要尝试 INSERT INTO items_for_sale VALUES('KYVGK0DBYXPMWW8','f');
这将失败,因为该序列号不存在于 toy_cars
或 toy_trains
.
如何做到这一点?我们更喜欢使用一个 table(就像现在的结构一样)。
这里的问题是您想要检查两个(其他)table 中是否存在一个键,并且您不能在单个列上使用外键约束来强制执行该操作。 Postgres 允许您为自定义检查创建 CHECK
表达式,但作为 the manual says:
PostgreSQL does not support CHECK constraints that reference table data other than the new or updated row being checked. (...) If possible, use UNIQUE, EXCLUDE, or FOREIGN KEY constraints to express cross-row and cross-table restrictions.
If what you desire is a one-time check against other rows at row insertion, rather than a continuously-maintained consistency guarantee, a custom trigger can be used to implement that.
请参阅 this very related question 了解不同的解决方案。
某些数据库(MS SQL 服务器)允许您use a function in CHECK
expressions。那将是最佳选择,但 Postgres 不允许该语法。
对于 PostgreSQL,您需要创建一个 TRIGGER
,它将在某些内容插入 items_for_sale
时执行。另一方面,确实允许函数或交叉table检查。
看起来像这样:
CREATE TRIGGER check_serial_present
BEFORE INSERT ON items_for_sale
FOR EACH ROW
EXECUTE FUNCTION assert_presence_of_serial(); -- implement this function
另一个链接的问题也提到了一种无需触发器即可实现此目的的非常优雅的方法:
A clean solution without triggers: add redundant columns and include them in FOREIGN KEY constraints
我非常喜欢它,因为它在概念上非常简单且易于掌握。我认为它只涉及这些步骤:
ALTER TABLE
添加两列:toy_car_serial
和toy_trains_serial
,均为 NULLABLE,但对上述 table 具有FOREIGN KEY
约束。- 确保任何
INSERT
都会将序列号插入serial_no
和toy_car_serial
或serial_no
和toy_car_serial
. - 添加
CHECK( toy_car_serial = serial_no OR toy_trains_serial = serial_no)
.
我认为两个冗余行和对您的插入内容的轻微修改比替代方法要少得多。