我记得在播客014中听到Joel Spolsky提到他几乎从未使用过外键(如果我没记错的话)。然而,对我来说,它们对于避免数据库中的重复和后续数据完整性问题非常重要。

人们是否有一些可靠的理由(以避免与Stack Overflow原则一致的讨论)?

编辑:“我还没有创建外键的理由,所以这可能是我真正建立一个外键的第一个理由。”


当前回答

“在添加记录之前,检查对应的记录是否存在于另一个表中”是业务逻辑。

这里有一些你不希望在数据库中使用它的原因:

If the business rules change, you have to change the database. The database will need to recreate the index in a lot of cases and this is slow on large tables. (Changing rules include: allow guests to post messages or allow users to delete their account despite having posted comments, etc). Changing the database is not as easy as deploying a software fix by pushing the changes to the production repository. We want to avoid changing the database structure as much as possible. The more business logic there is in the database the more you increase the chances of needing to change the databae (and triggering re-indexing). TDD. In unit tests you can substitute the database for mocks and test the functionality. If you have any business logic in your database, you are not doing complete tests and would need to either test with the database or replicate the business logic in code for testing purposes, duplicating the logic and increasing the likelyhood of the logic not working in the same way. Reusing your logic with different data sources. If there is no logic in the database, my application can create objects from records from the database, create them from a web service, a json file or any other source. I just need to swap out the data mapper implementation and can use all my business logic with any source. If there is logic in the database, this isn't possible and you have to implement the logic at the data mapper layer or in the business logic. Either way, you need those checks in your code. If there's no logic in the database I can deploy the application in different locations using different database or flat-file implementations.

其他回答

如果您绝对确信底层数据库系统将来不会改变,我将使用外键来确保数据完整性。

但在现实生活中,还有一个完全不使用外键的很好的理由:

您正在开发一个产品,该产品应该支持不同的数据库系统。

如果您正在使用实体框架(Entity Framework),该框架能够连接到许多不同的数据库系统,那么您可能还希望支持“开源免费”的无服务器数据库。并非所有这些数据库都支持外键规则(更新、删除行……)。

这会导致不同的问题:

1)。在创建或更新数据库结构时,可能会遇到错误。可能只有无声错误,因为数据库系统忽略了外键。

2)。如果依赖于外键,则可能在业务逻辑中较少甚至不进行数据完整性检查。现在,如果新的数据库系统不支持这些外键规则,或者只是以不同的方式运行,那么您必须重写业务逻辑。

您可能会问:谁需要不同的数据库系统?当然,不是每个人都能负担得起或想要在他的机器上安装一个完整的SQL-Server。这是软件,需要维护。其他人已经在其他一些DB系统上投入了时间和金钱。无服务器数据库非常适合只有一台机器上的小客户。

没有人知道,所有这些DB系统是如何运行的,但是您的业务逻辑,包括完整性检查,总是保持不变的。

Quite often we receive the errors with FK constraints Cannot add or update a child row: a foreign key constraint fails Suppose there are two tables inventory_source and contract_lines, and we are referring inventory_source_id in contract_lines from inventory_source and suppose we want to delete record from inventory_source and the record is already present in contract_lines or we want to delete the PK column from Base table, we get errors for FK constraints, we can avoid it using the steps jotted below.

CREATE TABLE inventory_source (
inventory_source_id int(11) NOT NULL AUTO_INCREMENT,
display_name varchar(40) NOT NULL,
state_id int(11) NOT NULL,
PRIMARY KEY (inventory_source_id),
KEY state_id (state_id),
CONSTRAINT ba_inventory_source_state_fk FOREIGN KEY (state_id) REFERENCES   ba_state (state_id)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

CREATE TABLE contract_lines(
contract_line_id int(11) NOT NULL AUTO_INCREMENT,
inventory_source_id int(11) NULL ,
PRIMARY KEY (contract_line_id),
UNIQUE KEY contract_line_id (contract_line_id),
KEY AI_contract_line_id (contract_line_id),
KEY contract_lines_inventory_source_fk (inventory_source_id),
CONSTRAINT contract_lines_inventory_source_fk FOREIGN KEY       (inventory_source_id) REFERENCES ba_inventory_source (inventory_source_id)
) ENGINE=InnoDB AUTO_INCREMENT=135 DEFAULT CHARSET=utf8 ;

我们可以采用以下步骤克服它:-

Delete or update the row from the inventory_source will automatically delete or update the matching rows in the contract_lines table and this is known as cascade delete or update. Another way of doing it is setting the column i.e inventory_source_id in the contract_lines table to NULL, when a record corresponding to it is deleted in the inventory_source table. We can restrict the parent table for delete or update in other words one can reject the delete or update operation for the inventory_source table. Attempt to delete or update a primary key value will not be permitted to proceed if there is a related foreign key value in the referenced table.

它们会使删除记录变得更加麻烦——当其他表中存在外键违反约束的记录时,您就不能删除“主”记录。可以使用触发器进行级联删除。

如果不明智地选择主键,则更改该值将变得更加复杂。例如,如果我有我的“客户”表的PK作为人的名字,并使该键在“订单”表中为FK,如果客户想更改他的名字,那么这是一个巨大的痛苦……但这只是粗制滥造的数据库设计。

我相信使用火密钥的优点大于任何假定的缺点。

我可以看到使用外键的一些原因(有人提到,孤立行很烦人),但我也从不使用它们。对于一个相对正常的DB模式,我不认为它们是100%需要的。约束是好的,但我认为通过软件来实施约束是更好的方法。

亚历克斯

有一个很好的理由不使用它们:如果你不了解它们的作用或如何使用它们。

在错误的情况下,外键约束可能导致事故的瀑布式复制。如果有人删除了错误的记录,恢复它可能会成为一项艰巨的任务。

同样,相反,当您需要删除某些内容时,如果设计不当,约束可能会导致各种锁阻止您的操作。