如果我总是用PHP控制我的数据库输入,那么像NOT NULL和FOREIGN KEY这样的约束条件有多重要?

24

我试图在一个表中创建一个外键列,在MySQL中这比它应该的要困难得多。这将要求我回去对已经使用的表进行某些更改。所以我想知道,MySQL确保某个值是否适当有多重要?我是否可以只使用像PHP这样的语言来访问这个数据库,从而做到这一点呢?

同样道理,如果我只用PHP访问这个数据库,我能不能简单地让PHP确保没有空值被输入呢?

为什么我要使用MySQL来执行这些约束条件,而不是用PHP来完成呢?


我意识到由于上述原因忽略NOT NULL是非常愚蠢的。但是,如果不进行严格操作,MySQL不会强制执行外键约束。

在您的意见中,使用“伪”外键,并简单地检查要输入的值是否在其他表中匹配,然后再用PHP是否仍然是不好的呢?


1
尝试在PHP中进行补偿会导致疯狂。MySQL非常容忍滑动的DDL更改。 - dkretz
15个回答

56

使用PHP的过程中,您肯定会犯错误,这是100%的保证。而PHP属于过程式编程范畴,你需要的是声明性约束。你希望告诉整个技术栈:"这些是数据上的约束条件,不能违反这些约束条件。"你不想用"步骤1...步骤2...步骤3...步骤432..."来处理数据上的约束条件,因为:

  • 你会做错
  • 当你后续对其进行更改时,你可能会忘记现在所作的修改
  • 除了你自己,没有人会像你现在这样完全了解所有这些隐含的约束条件,包括你未来的自己
  • 要正确并且一直强制执行这些约束条件需要大量的代码 - 数据库服务器已经有这些代码,但是你准备好编写吗?

实际上应该问的问题是:"为什么我要使用PHP来强制实施这些约束条件呢,我可以直接用MySQL解决吗?"


1
你如何将抛出的异常解释为有意义的错误消息(而不需要添加比首次获得的代码更多的代码)?你是否不相信在客户端进行验证? - dkretz
3
这不是问题的关键,你可以进行验证,但如果你遗漏了任何东西,你并不会危及数据的完整性,只是为用户带来一些烦恼的错误。 - ninesided
1
安全性是分层实现的,数据完整性也应该如此。 如果客户端验证可以被绕过,那么 PHP 应该进行验证。数据库完整性约束应该捕获其余部分。 - Calyth
4
如果你想捕捉错误,找出问题,并向用户显示有意义的信息,那么请务必在 PHP 中实现检查和验证。但是,如果这是你唯一实现它的地方,你肯定会出错。 - yfeldblum
1
在某种程度上,我不同意,你应该在代码和数据库中都有约束条件。但将约束条件放在数据库中的另一个原因是,您可能会有另一个应用程序与同一数据库相冲突,而您可能并不完全信任该应用程序。 - justin.m.chase
是的,你应该在数据库中设置约束,并编写代码在应用程序级别强制执行这些约束,而不仅仅依靠数据库服务器来执行。但是,由于它很难,你可能会在代码层面上实施有误,因此请在数据库中设置约束。 - yfeldblum

18

你不能仅使用PHP就能做到这一点,原因与程序员“仅仅”无法编写没有缺陷的代码相同。这比你想象的更难。特别是如果你认为这不那么难。


如果你写不出一些防错的插入\更新代码,那么你应该把精力集中在游戏软件上,而不是数据库管理程序上。 - Philippe Grondier
14
游戏软件可能更加困难。无论如何,一旦世界发现了菲利普·格朗迪的无bug代码,我就会失业。 - recursive

8
如果你可以确定绝对不会有其他途径访问数据库,那么仅使用PHP也是可以的(当然必须保证没有漏洞)。但实际情况中总会存在一些不确定性,因此让数据库服务器来维护数据的完整性是很好的选择。
对于简单的数据库而言,强制引用完整性并不是必需的,但这是一个很好的附加条件。随着应用程序变得越来越复杂,你将从中获得更多好处,并且在早期规划它们将使后续工作更容易。
此外,引用完整性可以迫使你按照规范设计数据库,因为不再可能使用任何不正当手段。这也是一件好事。

2

它们非常重要。您不想完全通过PHP定义模型。如果您的PHP代码中有错误怎么办?您可能会轻易地将列空置,而您的业务规则规定不应该这样做。通过在数据库级别定义,您至少可以免费获得该检查。当您的PHP或任何其他工具使用您的数据库时出现错误时,您将非常讨厌它。在我看来,您只是在寻求问题。

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


2
实施数据库约束非常重要,因为未来是不可预测的!你永远不知道你的需求何时会改变。
此外,还要考虑您可能有多个开发人员在同一应用程序上工作。你可能知道所有的约束条件,但初级开发人员可能不知道。如果在数据库中设置了约束条件,初级开发人员的代码将生成错误,他会知道需要修复什么。如果没有约束条件,代码可能不会失败,数据可能会受到损坏。

2

通常我倾向于在数据库中声明约束。以下是声明约束的理由:

  • 声明式代码比命令式代码更容易编写没有错误。即使应用程序代码存在错误,也会强制执行约束。
  • 支持“不要重复自己”的原则,如果您有多个应用程序或代码模块访问同一数据库,并且需要统一执行业务规则。如果您需要更改约束,则可以在一个地方进行更改,即使您有多个应用程序。
  • 即使人们尝试使用特定查询工具操纵数据库,也可以强制执行数据完整性。
  • 强制执行一致性,这意味着在任何数据更新之前和之后,您都可以确定数据处于有效状态。如果您不使用约束,则可能需要运行定期查询以检查破损的引用并清理它们。
  • 您可以使用约束轻松建模级联更新/删除。在应用程序代码中执行相同操作很复杂且效率低下,不能以原子方式应用更改(虽然建议使用事务隔离),而且容易出错。
  • 约束有助于使数据库更加自文档化,就像列名和SQL数据类型一样。

反对约束的理由:

  • 声明式约束无法建模更复杂的业务规则,因此您仍然需要在应用程序空间中实现某些规则。既然如此,为什么不在一个地方(您的应用程序)和同一种语言中实现所有业务规则呢?这样可以更容易地进行调试、测试和跟踪代码修订。
  • 约束通常涉及索引,在插入/更新期间会产生一定的开销。另一方面,即使您不声明约束,您可能仍需要索引,因为该列可能经常用于搜索条件或连接条件中。
  • 约束可能会使您清理数据错误变得更加复杂。
  • 在当前项目中,MyISAM与InnoDB相对于引用约束的不兼容性正在引起一些麻烦。

1

对我来说,使用 NOT NULL 最重要的是文档部分。当我几个月后回到项目时,我会忘记哪些列可以有空值。如果该列为 NOT NULL,则我知道我永远不必处理潜在的空值。如果它允许空值,则我确定必须处理它们。

另一件事是,正如其他人所指出的:你可能会错过某些地方,清理数据很麻烦,或者完全不可能。最好确保数据库中的所有数据都是一致的。


1

在MySQL中启用这些约束几乎不花费任何时间。即使它们能够防止因PHP或其他代码出现单个错误,这难道不值得吗?

请记住,您将从中挽救自己的那些错误可能非常严重。找到并修复错误本身可能并不难; 麻烦的是,一旦您修复了错误,就会留下一堆可能无法挽救的错误数据。

我甚至不会从“嗯,除了PHP还可能有其他东西访问您的数据”角度来解决这个问题。虽然这是事实,但在我的看法中,更重要的是可以通过添加一些简单的约束来节省自己的头疼、时间(金钱)和数据损失。


我问了一些人如何在MySQL中启用外键,这是相当困难的。两个表都必须是InnoDB,而我被告知这是一个不好的想法。此外,被引用的表已经被创建并保存了数据。有人能帮忙吗? - stalepretzel
1
为什么这是个坏主意呢?无论如何,“ALTER TABLE tablename ENGINE=InnoDB”可以将一个有数据的表从MyISAM转换为InnoDB。虽然它需要根据数据量来决定所需时间。 - Bill Karwin

1

使用数据库来保证结构数据的完整性,使用BR层处理其余部分。并尽早捕获错误。它们共同工作。

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


1

通过约束条件强制数据一致性有助于确保您的数据保持一致,并为应用程序提供廉价的运行时 bug 检查。

如果您认为约束条件不值得,那么要么你拥有一个小的 / 非关键任务系统,要么您正在错过提高系统质量的巨大机会。这一点不容忽视。

选择包括:选择不同的 RDBMS,重新发明自己的元数据系统或手动管理约束条件。在没有元数据系统的情况下进行查询的手动管理很快就会变得难以维护和审计,随着架构/系统复杂性的增长,这样做也会不必要地使演进中的模式变得更加复杂。

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

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

说到底,即使您在手动检查上花费了适度的努力,最有可能的结果仍然是,您仍然能够通过您未考虑或在形成时犯了错误的边角情况。

关于您的非空问题... 显然的数据字段要求是一个很好的起点。在定义列可空性时,还有一些其他需要考虑的事情。
它提供了一个保证,在编写查询时可能非常有帮助。各种连接可以使用 NULL 条件来显示表行的非匹配,与条件允许 nulls 的 NULL 值分开。(如果允许 NULL,则匹配可能意味着该行不匹配或该行匹配但列值为空。)
使用 NOT NULL 还有助于定义简单查询匹配值的规则。由于如果 value1 和 value2 都为 NULL,则不能说 "WHEN value1 = value2",则评估结果仍然为 false。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接