我经常在数据库中遇到一种情况,其中给定的表可以 FK 到许多不同的父表中的一个。我已经看到了这个问题的两种解决方案,但都不是个人满意的。我很好奇你在那里看到了什么其他模式?有没有更好的方法来做到这一点?
一个人为的例子
假设我的系统有Alerts
. 可以接收各种对象的警报——客户、新闻和产品。一个给定的警报可以只针对一个项目。无论出于何种原因,客户、文章和产品都在快速移动(或本地化),因此在创建警报时无法将必要的文本/数据拉入警报。鉴于此设置,我看到了两种解决方案。
注意:下面的 DDL 是针对 SQL Server 的,但我的问题应该适用于任何 DBMS。
解决方案 1 -- 多个可空 FKey
在此解决方案中,链接到多个表之一的表具有多个 FK 列(为简洁起见,下面的 DDL 不显示 FK 创建)。 好处- 在这个解决方案中,我有外键很好。FK 的空优化使得添加准确数据变得方便且相对容易。THE BAD Querying 不是很好,因为它需要N LEFT JOINS 或N UNION 语句来获取关联数据。在 SQL Server 中,特别是 LEFT JOINS 阻止创建索引视图。
CREATE TABLE Product (
ProductID int identity(1,1) not null,
CreateUTC datetime2(7) not null,
Name varchar(100) not null
CONSTRAINT PK_Product Primary Key CLUSTERED (ProductID)
)
CREATE TABLE Customer (
CustomerID int identity(1,1) not null,
CreateUTC datetime2(7) not null,
Name varchar(100) not null
CONSTRAINT PK_Customer Primary Key CLUSTERED (CustomerID)
)
CREATE TABLE News (
NewsID int identity(1,1) not null,
CreateUTC datetime2(7) not null,
Name varchar(100) not null
CONSTRAINT PK_News Primary Key CLUSTERED (NewsID)
)
CREATE TABLE Alert (
AlertID int identity(1,1) not null,
CreateUTC datetime2(7) not null,
ProductID int null,
NewsID int null,
CustomerID int null,
CONSTRAINT PK_Alert Primary Key CLUSTERED (AlertID)
)
ALTER TABLE Alert WITH CHECK ADD CONSTRAINT CK_OnlyOneFKAllowed
CHECK (
(ProductID is not null AND NewsID is null and CustomerID is null) OR
(ProductID is null AND NewsID is not null and CustomerID is null) OR
(ProductID is null AND NewsID is null and CustomerID is not null)
)
Run Code Online (Sandbox Code Playgroud)
解决方案 2——每个父表中的一个 FK
在这个解决方案中,每个“父”表都有一个到 Alert 表的 FK。它可以轻松检索与父级关联的警报。不利的一面是,从警报到引用者之间没有真正的链条。此外,数据模型允许孤立警报——其中警报与产品、新闻或客户无关。同样,多个 LEFT JOIN 来找出关联。
CREATE TABLE Product (
ProductID int identity(1,1) not null,
CreateUTC datetime2(7) not null,
Name varchar(100) not null
AlertID int null,
CONSTRAINT PK_Product Primary Key CLUSTERED (ProductID)
)
CREATE TABLE Customer (
CustomerID int identity(1,1) not null,
CreateUTC datetime2(7) not null,
Name varchar(100) not null
AlertID int null,
CONSTRAINT PK_Customer Primary Key CLUSTERED (CustomerID)
)
CREATE TABLE News (
NewsID int identity(1,1) not null,
CreateUTC datetime2(7) not null,
Name varchar(100) not null
AlertID int null,
CONSTRAINT PK_News Primary Key CLUSTERED (NewsID)
)
CREATE TABLE Alert (
AlertID int identity(1,1) not null,
CreateUTC datetime2(7) not null,
CONSTRAINT PK_Alert Primary Key CLUSTERED (AlertID)
)
Run Code Online (Sandbox Code Playgroud)
这只是关系数据库中的生活吗?您是否发现了更令人满意的替代解决方案?
小智 4
我理解第二种解决方案不适用,因为它不提供一对多(警报)关系。
由于严格的 3NF 合规性,您只能采用两种解决方案。
我会设计一个耦合度较低的模式:
CREATE TABLE Product (ProductID int identity(1,1) not null, ...)
CREATE TABLE Customer (CustomerID int identity(1,1) not null, ...)
CREATE TABLE News (NewsID int identity(1,1) not null, ...)
CREATE TABLE Alert (
-- See (1)
-- AlertID int identity(1,1) not null,
AlertClass char(1) not null, -- 'P' - Product, 'C' - Customer, 'N' - News
ForeignKey int not null,
CreateUTC datetime2(7) not null,
-- See (2)
CONSTRAINT PK_Alert Primary Key CLUSTERED (AlertClass, ForeignKey)
)
-- (1) you don't need to specify an ID 'just because'. If it's meaningless, just don't.
-- (2) I do believe in composite keys
Run Code Online (Sandbox Code Playgroud)
或者,如果完整性关系是强制性的,我可能会设计:
CREATE TABLE Product (ProductID int identity(1,1) not null, ...)
CREATE TABLE Customer (CustomerID int identity(1,1) not null, ...)
CREATE TABLE News (NewsID int identity(1,1) not null, ...)
CREATE TABLE Alert (
AlertID int identity(1,1) not null,
AlertClass char(1) not null, /* 'P' - Product, 'C' - Customer, 'N' - News */
CreateUTC datetime2(7) not null,
CONSTRAINT PK_Alert Primary Key CLUSTERED (AlertID)
)
CREATE TABLE AlertProduct (AlertID..., ProductID..., CONSTRAINT FK_AlertProduct_X_Product(ProductID) REFERENCES Product)
CREATE TABLE AlertCustomer (AlertID..., CustomerID..., CONSTRAINT FK_AlertCustomer_X_Customer(CustomerID) REFERENCES Customer)
CREATE TABLE AlertNews (AlertID..., NewsID..., CONSTRAINT FK_AlertNews_X_News(NewsID) REFERENCES News)
Run Code Online (Sandbox Code Playgroud)
反正...
三个有效的解决方案加上另一个要考虑的许多(对象)对一(警报)关系......
这些呈现出来的寓意是什么?
它们略有不同,但在标准上的权重相同:
所以,选择那个对你来说更舒服的。