[英]REFERENCES two different columns in two different tables
In a Postgresql database we have a table like below:在 Postgresql 数据库中,我们有一个如下表:
=# 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
Note:笔记:
Every serial_no
column is the primary key of that table and in_stock
is a boolean.每个serial_no
列都是该表的主键,而in_stock
是 boolean。
serial_no
in the toy_cars
table has a regex CHECK
restraint to allow 10 characters only. toy_cars
表中的serial_no
有一个正则表达式CHECK
限制,只允许 10 个字符。
serial_no
in the toy_trains
table has a regex CHECK
restraint to allow 15 characters only. toy_trains
表中的serial_no
有一个正则表达式CHECK
限制,只允许 15 个字符。
serial_no
in the items_for_sale
table is the serial of either the toy cars or trains, and has a regex CHECK
restraint to allow 10 or 15 characters only. items_for_sale
表中的serial_no
是玩具汽车或火车的序列号,并且具有正则表达式CHECK
限制,仅允许 10 或 15 个字符。
All serial_no
columns have the UNIQUE
restraint.所有serial_no
列都有UNIQUE
约束。
We want to add a REFERENCES
check to serial_no
in the items_for_sale
table to make sure that the entered serial is either present in the toy_cars
table OR the toy_trains
table.我们想在items_for_sale
表中为serial_no
添加一个REFERENCES
检查,以确保输入的序列号存在于toy_cars
表或toy_trains
表中。
So, if I were to try INSERT INTO items_for_sale VALUES('KYVGK0DBYXPMWW8','f');
所以,如果我要尝试INSERT INTO items_for_sale VALUES('KYVGK0DBYXPMWW8','f');
this would fail because that serial is not present in either toy_cars
or toy_trains
.这将失败,因为该序列不存在于toy_cars
或toy_trains
中。
How can this be done?如何才能做到这一点? We prefer to use one table (like it's structured now).我们更喜欢使用一张表(就像它现在的结构一样)。
The problem here is that you want to check presence of a key in two (other) tables, and you cannot enforce that using foreign key constraints on a single column.这里的问题是您想检查两个(其他)表中是否存在键,并且您无法在单个列上使用外键约束来强制执行该操作。 Postgres allows you to create CHECK
expressions for custom checks, but as the manual says : Postgres 允许您为自定义检查创建CHECK
表达式,但正如手册所述:
PostgreSQL does not support CHECK constraints that reference table data other than the new or updated row being checked. PostgreSQL 不支持引用表数据的 CHECK 约束,而不是正在检查的新行或更新行。 (...) If possible, use UNIQUE, EXCLUDE, or FOREIGN KEY constraints to express cross-row and cross-table restrictions. (...) 如果可能,请使用 UNIQUE、EXCLUDE 或 FOREIGN KEY 约束来表示跨行和跨表限制。
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.如果您想要的是在插入行时对其他行进行一次性检查,而不是持续保持一致性保证,则可以使用自定义触发器来实现它。
See this very related question for different solutions.有关不同的解决方案,请参阅这个非常相关的问题。
Some databases (MS SQL Server) allow you to use a function in CHECK
expressions .某些数据库(MS SQL 服务器)允许您在CHECK
表达式中使用 function 。 That would be optimal, but Postgres does not allow that syntax.那将是最佳的,但 Postgres 不允许这种语法。
For PostgreSQL, you need to create aTRIGGER
that will execute when something is inserted into items_for_sale
.对于 PostgreSQL,您需要创建一个TRIGGER
,该触发器将在将某些内容插入items_for_sale
时执行。 That, on the other hand, does allow functions or cross-table checks.另一方面,这确实允许函数或跨表检查。
It would look something like this:它看起来像这样:
CREATE TRIGGER check_serial_present
BEFORE INSERT ON items_for_sale
FOR EACH ROW
EXECUTE FUNCTION assert_presence_of_serial(); -- implement this function
The other linked question also mentions a quite elegant way of achieving this without triggers:另一个链接的问题还提到了一种非常优雅的方式来实现这一点而无需触发器:
A clean solution without triggers: add redundant columns and include them in FOREIGN KEY constraints没有触发器的干净解决方案:添加冗余列并将它们包含在 FOREIGN KEY 约束中
I quite like this as it's conceptually very simple and easy to grasp.我非常喜欢这个,因为它在概念上非常简单且易于掌握。 I think it would just involve these steps:我认为它只涉及以下步骤:
ALTER TABLE
to add two columns: toy_car_serial
and toy_trains_serial
, both NULLABLE, but with FOREIGN KEY
constraints on the mentioned tables. ALTER TABLE
添加两列: toy_car_serial
和toy_trains_serial
,都是 NULLABLE,但在提到的表上有FOREIGN KEY
约束。INSERT
will insert a serial into both serial_no
and toy_car_serial
OR serial_no
and toy_car_serial
.确保任何INSERT
都会将序列插入serial_no
和toy_car_serial
或serial_no
和toy_car_serial
。CHECK( toy_car_serial = serial_no OR toy_trains_serial = serial_no)
.添加CHECK( toy_car_serial = serial_no OR toy_trains_serial = serial_no)
。I think two redundant rows and a slight modification to your inserts is a lot less involved than the alternative.我认为两个冗余行和对插入的轻微修改比替代方案少得多。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.