23

我正在尝试在作为外键的表中创建一个列,但在 MySQL 中这比它应该的要困难。这将需要我返回并对已使用的表进行某些更改。所以我想知道,MySQL 是否有必要确保某个值是合适的?难道我不能只用像 PHP 这样的语言来做到这一点吗,无论如何我都用它来访问这个数据库

与 NOT NULL 类似。如果我只用 PHP 访问这个数据库,我不能简单地让 PHP 确保没有输入空值吗?

为什么我应该使用 MySQL 来强制执行这些约束,而我可以使用 PHP 来做到这一点?


由于上述原因,我意识到 NOT NULL 是一个非常愚蠢的部分。但是 MySQL 不会强制执行外键,除非有严重程度的胡闹。

在您看来,使用“假”外键并简单地检查要输入的值是否在其他表中与 PHP 匹配是否仍然很糟糕?

4

15 回答 15

56

在 PHP 上犯错,100% 保证。PHP 是程序化的。你想要的是声明性约束。你想告诉整个堆栈:“这些是对数据的约束,不能违反这些约束。” 作为对数据实施约束的方法,您不想过多使用“步骤 1 ... 步骤 2 ... 步骤 3 ... 步骤 432 ...”,因为

  • 你会弄错的
  • 当你以后改变它时,你会忘记你现在所做的
  • 没有人会像你现在知道的那样知道所有这些隐含的约束,包括你未来的自己
  • 需要大量代码才能始终正确地强制执行约束 - 数据库服务器已经拥有此代码,但您准备好编写它了吗?

这个问题的措辞实际上应该是“为什么我应该使用 PHP 来强制执行这些约束,而我可以只使用 MySQL 来做到这一点?”

于 2008-12-19T21:54:24.553 回答
18

你不能“仅仅”用PHP来做这件事,就像程序员“只是”不能写出没有错误的代码一样。这比你想象的要难。特别是如果你认为这并不难。

于 2008-12-19T21:58:19.707 回答
8

如果你可以发誓你的生命不会通过任何其他方式访问数据库,那么你的(当然是无错误的)PHP 页面,那么单独使用 PHP 就可以了。

由于现实世界的场景总是包含一些不确定性,因此最好让数据库服务器监视数据的完整性。

对于简单的数据库,参照完整性约束可能不是绝对的要求,而是一个不错的选择。应用程序越复杂,您从中获得的好处就越多。尽早计划它们会使您以后的生活更轻松。

此外,引用完整性是迫使您以更加按部就班的方式设计数据库的一部分,因为并非所有肮脏的黑客行为都成为可能。这也是一件好事。

于 2008-12-19T21:57:05.403 回答
2

它们非常重要。您不想完全通过 PHP 定义模型。如果您的 PHP 代码中有错误怎么办?您可以轻松地在您的业务规则声明不应该的地方设置空列。通过在数据库级别定义它,您至少可以免费获得该检查。当您的 PHP 中存在错误或任何其他工具曾经使用您的数据库时,您会非常讨厌它。你只是在问问题,恕我直言。

请注意,这是故事的非常简短的版本。

于 2008-12-19T21:54:25.097 回答
2

在数据库中实现约束很重要,因为无法预测未来!你永远不知道你的要求什么时候会改变。

还要考虑您可能有多个开发人员在同一应用程序上工作的可能性。您可能知道所有限制是什么,但初级开发人员可能不知道。由于数据库受到限制,初级开发人员的代码会产生错误,他会知道需要修复一些问题。没有约束,代码可能不会失败,数据可能会损坏。

于 2008-12-19T21:56:18.247 回答
2

我通常赞成在数据库中声明约束。约束的参数:

  • 声明式代码比命令式代码更容易消除错误。即使应用程序代码包含错误,也会强制执行约束。
  • 支持“不要重复自己”原则,如果您有多个应用程序或代码模块访问同一个数据库,并且您需要统一执行业务规则。如果您需要更改约束,即使您有许多应用程序,也可以在一个地方完成。
  • 即使人们试图绕过应用程序,也可以强制数据完整性,使用临时查询工具修改数据库。
  • 强制一致性,这意味着您始终可以确定数据在任何数据更新之前和之后都处于有效状态。如果您不使用约束,您可能需要运行定期查询来检查损坏的引用并清理它们。
  • 您可以使用约束轻松建模级联更新/删除。在应用程序代码中做同样的事情既复杂又低效,不能原子地应用更改(尽管建议使用事务隔离),并且容易出现错误。
  • 约束有助于数据库更加自我记录,就像列名和 SQL 数据类型有帮助一样。

反对约束的论点:

  • 更复杂的业务规则无法通过声明性约束建模,因此无论如何您都必须在应用程序空间中实现一些。鉴于此,为什么不在一个地方(您的应用程序)并以相同的语言实现所有业务规则?这使得调试、测试和跟踪代码修订变得更加容易。
  • 约束通常涉及索引,这会在插入/更新期间产生一些开销。另一方面,即使您不声明约束,您也可能无论如何都需要索引,因为该列可能经常用于搜索条件或连接条件。
  • 约束可能会使您尝试“清理”数据中的错误变得复杂。
  • 在您当前的项目中,MyISAM 与 InnoDB 在引用约束方面的不兼容导致了一些问题。
于 2008-12-19T23:09:30.843 回答
1

对我来说,使用 NOT NULL 最重要的是文档部分。几个月后我返回项目时,我忘记了哪些列可以包含空值。如果该列显示 NOT NULL,那么我知道我永远不必处理其中的潜在空值。如果它允许为空,那么我肯定知道我必须处理它们。

另一件事是,正如其他人所指出的:您可能会在某处遗漏某些东西,清理数据很糟糕,或者可能完全不可能。最好确定数据库中的所有数据都是一致的。

于 2008-12-19T22:00:28.830 回答
1

在 MySQL 中启用这些约束几乎需要零时间。如果由于错误的 PHP 或其他代码,它们甚至可以使您免于一个错误,那不值得吗?

请记住,您将从中拯救自己的各种错误可能非常令人讨厌。查找和修复错误本身可能并不难;令人讨厌的是,一旦你修复了这个错误,你就会留下一堆甚至可能无法挽救的错误数据。

我什至不会从“好吧,有朝一日 PHP 以外的东西可能会访问您的数据”的角度来解决这个问题。确实如此,但在我看来,更重要的是头痛、时间(金钱)和数据丢失,您只需添加一些简单的约束即可节省您自己。

于 2008-12-19T22:02:37.920 回答
1

将数据库用于结构数据完整性,其余部分使用 BR 层。并尽早发现错误。他们一起工作。

幸运的是,当您的代码成熟时,您不会遇到数据库 RI 错误;你可以自豪地宣布自己是第一个。

于 2008-12-19T22:03:28.963 回答
1

让您的数据层通过约束强制执行数据一致性有助于确保您的数据保持一致,并在您的应用程序中提供廉价的运行时错误检查。

如果您认为约束不值得,那么您要么拥有一个小型/非关键任务系统,要么您正在放弃提高系统质量的巨大机会。这不能被低估。

选择包括:选择不同的 RDBMS、重新发明您自己的元数据系统或手动管理约束。随着模式/系统复杂性的增长以及不必要地使不断发展的模式复杂化,在没有元数据系统的情况下手动管理查询很快变得无法正确维护和审计。

我的建议是选择不同的 RDBMS。

一致性检查比您想象的要困难得多。例如 MySQL 使用事务读取一致性,这意味着您检查的值可能与另一个事务范围内的值不同。如果不直接绑定到数据层,则并发访问的一致性语义非常难以正确。

当一切都说完了,即使在人工检查上投入了少量的努力,可能的结果是,人们仍然能够驾驶卡车通过你没有考虑到或在成型时犯了错误的极端情况。

关于您的 NOT NULL 问题...明显的数据字段要求是一个很好的起点。以下是定义列可空性时要考虑的其他几件事。

它提供了一种保证,在编写查询时非常有用。各种连接可以使用 NULL 条件来显示与 NULL 值分开的表行的不匹配,如果条件允许空值,则不能假定该值。(如果允许 NULL,则匹配可能意味着行不匹配或行匹配但列值为空。)

NOT NULL 的使用还有助于为更简单的查询匹配值定义规则。由于如果 value1 和 value2 都为 NULL,则您不能说“WHEN value1 = value2”,因此评估结果仍然是错误的。

于 2008-12-22T07:04:25.843 回答
1

即使您的 PHP 代码完全没有错误,它也可能会在脚本中间停止(内存不足错误、某些库中的段错误等),在数据库中留下一半插入的内容,因此使用 InnoDB 和事务非常重要。

约束也一样,当然你应该有适当的表单验证,以及它背后的数据库约束来捕捉错误。

数据库约束很容易指定,在应用程序中查找错误很困难,没有约束就更难了。

我的经验是,约束不当的数据库,以及任何使用 MyISAM 的东西,在使用几个月后都会有不一致的数据,而且很难找到它的来源。

于 2009-07-25T10:58:51.377 回答
0
  1. 我认为您不能确定您的数据库只能由 PHP 访问,如果是这样,开发人员将使用它来尊重数据库整个生命周期的这些约束。

  2. 如果您在架构中包含这些约束,则可以通过调查您的架构来很好地了解数据是如何使用和关联的。如果您只将所有这些都放在代码中,那么有人将不得不同时查看数据库和 PHP 代码。

但是这些东西不应该在设计文档、数据字典和逻辑数据库设计中吗?

是的,但这些文件因过时和陈旧而臭名昭著。我知道永远不会允许这种情况发生,但是一些有经验的项目经验较少的人可能会认为你的项目是这样的,并且想要查阅实际的代码和架构而不是文档。

于 2008-12-19T22:16:44.850 回答
0

我非常感谢您的问题,因为我深信默认值规则应该在代码端实现,而不是在数据库端实现,原因很简单:当用户是发起数据库更改的人时( INSERTS、SELECTS 和 UPDATES),这些变化应该整合所有的业务规则,默认值基本上是业务规则:

  • 没有发票号码就没有发票
  • 没有没有数量的发票行,不接受 0 或 null
  • 没有接收日期就没有收到的邮件
  • ETC

几年前,我们决定摆脱所有这些“数据库端”的人工制品,如“not null”、“(不允许)允许空字符串”和其他“默认值”技巧,并且效果很好。支持默认值的参数主要是指一种“安全”原则(“在数据库端这样做,因为你会在代码端忘记它/你的语言不是为此而设计的/这样做更容易在数据库端”)一旦您选择不在数据库端实现任何默认值就没有任何意义:只需在调试时检查您的业务规则是否正确实现。

在过去的 2 年里,团队中没有人想过在表中声明一个默认值。我猜我们年轻的实习生甚至不知道所谓的“默认值”。

编辑:在这里重读一些答案,我的最后评论是:在任何方面做,无论是数据库还是代码,但做出你的选择,只做一面!没有什么比在双方都有这样的控制更危险的了,因为最终(1)你永远不会知道双方是否真的在执行相同的规则,这意味着(2)检查规则将意味着检查双方,这真的可以变得一团糟!最坏的情况当然是当一部分工作在数据库端完成(即创建数据库时确定的规则)而另一部分(即新确定的规则)在客户端完成......噩梦……

于 2008-12-19T22:34:53.317 回答
0

恐怕这是一个宗教话题。

从纯粹的角度来看,您希望数据库具有参照完整性。当您有多个应用程序访问数据库时,这是理想的,因为约束在一个地方。不幸的是,现实世界并不理想。

如果您必须强制执行某种参照完整性,根据我的经验,您的应用程序将需要知道如何执行此操作。这与它是否是最终仲裁者无关,或者数据库也会对其进行检查。即使数据库确实做到了引用完整性,那么应用程序也必须知道如果数据库拒绝更新该怎么办,因为引用完整性会被违反......

作为旁注,设置 MySQL 以支持外键约束是一个过程,因为您需要转移到InnoDB。如果你这样做你可以通过设置为 1 来获得很多性能innodb_flush_log_at_tx_commit。但如果你可以重新设计你的站点以支持事务,那可能会更好。然后你会得到 InnoDB 的两个好处。

于 2008-12-22T05:24:36.420 回答
0

在数据库级别实现默认值和约束;将导致任何消费应用程序可接受的数据的规则。这使您免受完整性问题的影响。

然后,在应用程序级别实现更好的默认值和约束。如果您在技术上被禁止(访问数据库外部的 API)实现约束或在数据库级别生成默认值,那么您需要在应用程序中执行此操作。这将产生更好的默认值。但是,应用程序的段错误或一般故障不会导致不可接受的数据被持久化。

于 2013-03-23T13:41:43.823 回答