简体   繁体   English

REFERENCES 两个不同表中的两个不同列

[英]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_carstoy_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:认为它只涉及以下步骤:

  1. 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_serialtoy_trains_serial ,都是 NULLABLE,但在提到的表上有FOREIGN KEY约束。
  2. Make sure that any INSERT will insert a serial into both serial_no and toy_car_serial OR serial_no and toy_car_serial .确保任何INSERT都会将序列插入serial_notoy_car_serialserial_notoy_car_serial
  3. Add 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM