确保 SQL 服务器中的两个列值相关
Ensuring that two column values are related in SQL Server
我正在使用 Microsoft SQL Server 2017,我很好奇如何约束特定关系。我在表达上有点困难,所以我更愿意通过一个例子来分享。
考虑以下假设的数据库。
Customers
+---------------+
| Id | Name |
+---------------+
| 1 | Sam |
| 2 | Jane |
+---------------+
Addresses
+----------------------------------------+
| Id | CustomerId | Address |
+----------------------------------------+
| 1 | 1 | 105 Easy St |
| 2 | 1 | 9 Gale Blvd |
| 3 | 2 | 717 Fourth Ave |
+------+--------------+------------------+
Orders
+-----------------------------------+
| Id | CustomerId | AddressId |
+-----------------------------------+
| 1 | 1 | 1 |
| 2 | 2 | 3 |
| 3 | 1 | 3 | <--- Invalid Customer/Address Pair
+-----------------------------------+
请注意,最后的 Order
将客户链接到不属于他们的地址。我正在寻找一种方法来防止这种情况。
(您可能会问为什么我需要 Orders
table 中的 CustomerId
。明确地说,我认识到 Address
已经为我提供了相同的信息,并且没有无效对的可能性。但是,我更愿意将 Order
扁平化,这样我就不必通过地址来检索客户。)
从我能够找到的相关阅读中,似乎一种方法可能是启用针对用户定义函数的 CHECK
约束。此用户定义的函数将类似于以下内容:
WHERE EXISTS (SELECT 1 FROM Addresses WHERE Id = Order.AddressId AND CustomerId = Order.CustomerId)
虽然我认为这会奏效,但考虑到我 能够找到的文章中的一些 "generality",我并不完全相信这是我的最佳选择。
另一种方法可能是从 Addresses
table 中完全删除 CustomerId
列,而是添加另一个 table 和 Id
,CustomerId
、AddressId
。然后 Order
将引用 this Id
。同样,我不喜欢必须通过辅助 table 来获得 Customer
或 Address
.
的想法
有没有更简洁的方法来做到这一点?还是我只是把这一切都弄错了?
好问题,但从根本上看,您似乎正在努力为不是外键的内容创建外键约束:
Orders.CustomerId -> Addresses.CustomerId
没有简单的 built-in 方法可以做到这一点,因为通常不会这样做。在理想的 RDBMS 实践中,您应该努力将特定类型的数据封装在它们自己的 tables only 中。换句话说,尽量避免冗余数据。
在上面的示例中,地址所有权在地址 table 和订单 table 中都是冗余的,因此需要额外的检查以保持它们同步。对于更大的数据集,这很容易失控。
您提到:
However, I'd prefer to have an Order flattened such that I don't have to channel through an address to retrieve a customer.
但这就是关系数据库是关系数据库的原因。它这样做是为了使不同的数据可以保持不同并使用相对 ID 进行引用。
我认为最好的解决办法就是简单地放弃这个要求。
换句话说,只需选择:
Customers
+---------------+
| Id | Name |
+---------------+
| 1 | Sam |
| 2 | Jane |
+---------------+
Addresses
+----------------------------------------+
| Id | CustomerId | Address |
+----------------------------------------+
| 1 | 1 | 105 Easy St |
| 2 | 1 | 9 Gale Blvd |
| 3 | 2 | 717 Fourth Ave |
+------+--------------+------------------+
Orders
+--------------------+
| Id | AddressId |
+--------------------+
| 1 | 1 |
| 2 | 3 |
| 3 | 3 | <--- Valid Order/Address Pair
+--------------------+
话虽如此,为了准确地实现您的目的,您确实有 视图 可用于此类事情:
create view CustomerOrders
as
select o.Id OrderId,
a.CustomerId,
o.AddressId
from Orders
join Addresses a on a.Id = o.AddressId
我知道这对于视图来说是一个非常微不足道的 use-case 但我想为其添加一个插件,因为它们经常被忽视并且在组织大数据集时派上用场。使用 WITH SCHEMABINDING
它们也可以被索引以提高性能。
You may ask why I need the CustomerId
in the Orders
table at all. To be clear, I recognize that the Address
already offers me the same information without the possibility of invalid pairs. However, I'd prefer to have an Order
flattened such that I don't have to channel through an address to retrieve a customer.
如果遇到性能问题,第一件事就是创建或修改适当的索引。并且 DBMS 通常擅长连接操作(具有适当的索引)。但是,是的,规范化有时可以帮助性能调整。但这应该是最后的手段。如果走那条路,一个人应该真正知道自己在做什么,并且要非常小心,不要在一天结束时造成更多损失,因为那个人已经获得了。我怀疑你在这里没有选择,真的需要走那条路。你可能找错了树。因此,我建议您采用 "normal"、"sane" 方式,只需将 customerid
放入 orders
并创建适当的索引。
但如果你真的坚持,你可以尝试让 (id, customerid)
成为 addresses
中的一个键(具有唯一约束),然后基于它创建一个外键。
ALTER TABLE addresses
ADD UNIQUE (id,
customerid);
ALTER TABLE orders
ADD FOREIGN KEY (addressid,
customerid)
REFERENCES addresses
(id,
customerid);
我正在使用 Microsoft SQL Server 2017,我很好奇如何约束特定关系。我在表达上有点困难,所以我更愿意通过一个例子来分享。
考虑以下假设的数据库。
Customers
+---------------+
| Id | Name |
+---------------+
| 1 | Sam |
| 2 | Jane |
+---------------+
Addresses
+----------------------------------------+
| Id | CustomerId | Address |
+----------------------------------------+
| 1 | 1 | 105 Easy St |
| 2 | 1 | 9 Gale Blvd |
| 3 | 2 | 717 Fourth Ave |
+------+--------------+------------------+
Orders
+-----------------------------------+
| Id | CustomerId | AddressId |
+-----------------------------------+
| 1 | 1 | 1 |
| 2 | 2 | 3 |
| 3 | 1 | 3 | <--- Invalid Customer/Address Pair
+-----------------------------------+
请注意,最后的 Order
将客户链接到不属于他们的地址。我正在寻找一种方法来防止这种情况。
(您可能会问为什么我需要 Orders
table 中的 CustomerId
。明确地说,我认识到 Address
已经为我提供了相同的信息,并且没有无效对的可能性。但是,我更愿意将 Order
扁平化,这样我就不必通过地址来检索客户。)
从我能够找到的相关阅读中,似乎一种方法可能是启用针对用户定义函数的 CHECK
约束。此用户定义的函数将类似于以下内容:
WHERE EXISTS (SELECT 1 FROM Addresses WHERE Id = Order.AddressId AND CustomerId = Order.CustomerId)
虽然我认为这会奏效,但考虑到我 能够找到的文章中的一些 "generality",我并不完全相信这是我的最佳选择。
另一种方法可能是从 Addresses
table 中完全删除 CustomerId
列,而是添加另一个 table 和 Id
,CustomerId
、AddressId
。然后 Order
将引用 this Id
。同样,我不喜欢必须通过辅助 table 来获得 Customer
或 Address
.
有没有更简洁的方法来做到这一点?还是我只是把这一切都弄错了?
好问题,但从根本上看,您似乎正在努力为不是外键的内容创建外键约束:
Orders.CustomerId -> Addresses.CustomerId
没有简单的 built-in 方法可以做到这一点,因为通常不会这样做。在理想的 RDBMS 实践中,您应该努力将特定类型的数据封装在它们自己的 tables only 中。换句话说,尽量避免冗余数据。
在上面的示例中,地址所有权在地址 table 和订单 table 中都是冗余的,因此需要额外的检查以保持它们同步。对于更大的数据集,这很容易失控。
您提到:
However, I'd prefer to have an Order flattened such that I don't have to channel through an address to retrieve a customer.
但这就是关系数据库是关系数据库的原因。它这样做是为了使不同的数据可以保持不同并使用相对 ID 进行引用。
我认为最好的解决办法就是简单地放弃这个要求。
换句话说,只需选择:
Customers
+---------------+
| Id | Name |
+---------------+
| 1 | Sam |
| 2 | Jane |
+---------------+
Addresses
+----------------------------------------+
| Id | CustomerId | Address |
+----------------------------------------+
| 1 | 1 | 105 Easy St |
| 2 | 1 | 9 Gale Blvd |
| 3 | 2 | 717 Fourth Ave |
+------+--------------+------------------+
Orders
+--------------------+
| Id | AddressId |
+--------------------+
| 1 | 1 |
| 2 | 3 |
| 3 | 3 | <--- Valid Order/Address Pair
+--------------------+
话虽如此,为了准确地实现您的目的,您确实有 视图 可用于此类事情:
create view CustomerOrders
as
select o.Id OrderId,
a.CustomerId,
o.AddressId
from Orders
join Addresses a on a.Id = o.AddressId
我知道这对于视图来说是一个非常微不足道的 use-case 但我想为其添加一个插件,因为它们经常被忽视并且在组织大数据集时派上用场。使用 WITH SCHEMABINDING
它们也可以被索引以提高性能。
You may ask why I need the
CustomerId
in theOrders
table at all. To be clear, I recognize that theAddress
already offers me the same information without the possibility of invalid pairs. However, I'd prefer to have anOrder
flattened such that I don't have to channel through an address to retrieve a customer.
如果遇到性能问题,第一件事就是创建或修改适当的索引。并且 DBMS 通常擅长连接操作(具有适当的索引)。但是,是的,规范化有时可以帮助性能调整。但这应该是最后的手段。如果走那条路,一个人应该真正知道自己在做什么,并且要非常小心,不要在一天结束时造成更多损失,因为那个人已经获得了。我怀疑你在这里没有选择,真的需要走那条路。你可能找错了树。因此,我建议您采用 "normal"、"sane" 方式,只需将 customerid
放入 orders
并创建适当的索引。
但如果你真的坚持,你可以尝试让 (id, customerid)
成为 addresses
中的一个键(具有唯一约束),然后基于它创建一个外键。
ALTER TABLE addresses
ADD UNIQUE (id,
customerid);
ALTER TABLE orders
ADD FOREIGN KEY (addressid,
customerid)
REFERENCES addresses
(id,
customerid);