确保SQL Server中的两个列值相关

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (27)

我正在使用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将客户链接到不属于他们的地址。我正在寻找一种方法来防止这种情况发生。

(你可能会问为什么我需要CustomerIdOrders表格中显示。为了清楚起见,我认识到Address已经提供了相同的信息而没有无效对的可能性。但是,我宁愿做一个Order扁平的,这样我就不会必须通过地址来检索客户。)

从我能够找到的相关阅读中,似乎一种方法可能是启用CHECK针对用户定义函数的约束。此用户定义的函数将类似于以下内容:

WHERE EXISTS (SELECT 1 FROM Addresses WHERE Id = Order.AddressId AND CustomerId = Order.CustomerId)

虽然我想象这会工作,因为我的文章有点“普遍性” 能找到的,我不觉得完全有信心,这是我最好的选择。

另一种可能是,除去CustomerId从柱Addresses完全表,而是添加另一个表IdCustomerIdAddressId。然后Order会引用 Id。再一次,我不喜欢通过辅助表来获取一个Customer或多个的想法Address

有更清洁的方法吗?或者我只是简单地说这一切都错了?

提问于
用户回答回答于

你可能会问,为什么我需要CustomerIdOrders表都没有。为了清楚起见,我认识到Address已经为我提供了相同的信息而没有无效对的可能性。但是,我更喜欢Order扁平化,以便我不必通过地址来检索客户。

如果遇到性能问题,首先要创建或修改正确的索引。DBMS通常擅长连接操作(具有适当的索引)。但是,规范化有时可以帮助进行性能调优。但它应该是最后的手段。如果采取这种方式,人们应该真正知道自己在做什么,并且要非常小心,不要在一天结束时损坏更多,一个人已经获得了。我怀疑,你在这里没有选择,真的需要走那条路。你可能会咆哮错误的树。因此,我建议你把“正常”,“理智”的方式,只是下降customeridorders,并创建适当的索引。

但是,如果你真的坚持,你可以尝试创建(id, customerid)一个密钥addresses(使用一个唯一的约束),然后根据它创建一个外键。

ALTER TABLE addresses
            ADD UNIQUE (id,
                        customerid);

ALTER TABLE orders
            ADD FOREIGN KEY (addressid,
                             customerid)
                            REFERENCES addresses
                                       (id,
                                        customerid);
用户回答回答于

好问题,但从根本上看,你似乎正在努力创建一个非外键的东西的外键约束:

Orders.CustomerId -> Addresses.CustomerId

没有简单的内置方法可以做到这一点,因为它通常没有完成。在理想的RDBMS的做法,你应该力争在自己的表来封装特定类型的数据。换句话说,尽量避免冗余数据。

在上面的示例情况中,地址所有权在地址表和订单表中都是多余的,因此需要进行额外的检查以使它们保持同步。这可能很容易与更大的数据集失控。

你提到过:

但是,我更倾向于将订单展平,以便我不必通过地址来检索客户。

但这就是关系数据库是关系数据库的原因。它这样做是为了使不同的数据可以保持不同并用相对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
+--------------------+

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励