在INSERT之前检查UNIQUE约束与检查

时间:2014-02-19 19:00:59

标签: mysql sql sql-server oracle

我有一个带有列的SQL服务器表RealEstate - Id,Property,Property_Value。该表有大约500万到1000万行,并且将来可以增加更多。我只想在此表中不存在Id,Property,Property_Value的组合时插入行。

示例表 -

1,Rooms,5
1,Bath,2
1,Address,New York
2,Rooms,2
2,Bath,1
2,Address,Miami

不应允许插入2,Address,Miami。但是,2,Price,2billion没问题。我很想知道哪种方法是“最好的”,为什么。为什么这部分对我来说最重要。两种检查方式是 -

  1. 在应用程序级别 - 应用程序应在插入行之前检查是否存在行。
  2. 在数据库级别 - 在所有3列上设置唯一约束并让数据库 做检查而不是人/ app。
  3. 是否有任何一种情况会比另一种更好?

    感谢。

    PS:我知道已经有类似的问题,但它没有回答我的问题 - Unique constraint vs pre checking 另外,我认为UNIQUE适用于所有数据库,所以我认为我不应该删除mysql和oracle标签。

4 个答案:

答案 0 :(得分:12)

我认为在大多数情况下,这两者之间的差异将足够小,以至于选择应该主要通过选择最初对于第一次查看代码的人来说最容易理解的实现来驱动。

但是,我认为异常处理有一些优势:

  • 异常处理可避免潜在的竞争条件。如果另一个进程在您的检查和插入之间插入记录,则“检查,然后插入”方法可能会失败。因此,即使您正在进行'检查然后插入',您仍然需要在插入上进行异常处理,如果您已经在进行异常处理,那么您最好还是取消初始检查。

  • 如果您的代码不是存储过程并且必须通过网络与数据库交互(即应用程序和数据库不在同一个框中),那么您希望避免两个单独的网络调用(一个用于检查,另一个用于插入)并通过异常处理完成,提供了一种通过单个网络调用处理整个事物的简单方法。现在,有很多方法可以执行“检查然后插入”方法,同时仍然避免第二次网络调用,但只是捕获异常可能是最简单的方法。

另一方面,异常处理需要一个唯一约束(实际上是一个唯一索引),它伴随着性能权衡:

  • 在非常大的表上创建唯一约束会很慢,并且会对该表的每个插入产生性能影响。在真正大型的数据库上,您还必须为用于强制执行约束的唯一索引所占用的额外磁盘空间进行预算。
  • 另一方面,如果您的查询可以利用该索引,则可以更快地从表中进行选择。

我还要注意,如果您处于实际想要做的事情是'更新其他插入'(即如果已存在具有唯一值的记录,那么您想要更新该记录,否则您插入新记录)然后你真正想要使用的是你的特定数据库的UPSERT方法,如果有的话。对于SQL Server和Oracle,这将是一个MERGE语句。

答案 1 :(得分:6)

取决于#1(进行查找)合理的成本,我会做两件事。至少,在Oracle中,这是我最熟悉的数据库。

理由:

  • 唯一/主键应该是数据模型设计的核心部分,我看不出任何不实现它们的理由 - 如果您有太多数据导致性能受到维护唯一索引的影响:
    • 这是很多的数据
    • 对其进行分区或将其存档远离OLTP工作
  • 您拥有的约束越多,您的数据对应用程序逻辑错误就越安全。
  • 如果您首先检查是否存在行,则可以轻松地从该行中提取其他信息以用作错误消息的一部分,或以其他方式分叉应用程序逻辑以应对重复。
  • 在Oracle中,回滚DML语句相对昂贵,因为Oracle希望默认情况下成功(即COMMIT已编写的更改)。

答案 2 :(得分:3)

这不能直接回答这个问题,但我认为将它发布在这里可能会有所帮助,因为它比维基百科更好,并且链接可能有一天会变得死亡。

链接 - http://www.celticwolf.com/blog/2010/04/27/what-is-a-race-condition/

维基百科对竞争条件有很好的描述,但如果你不了解编程的基础知识就很难理解。我将尝试用较少技术术语来解释它,使用如上所述生成标识符的示例。我还会将人类活动的类比用于尝试传达这些想法。

竞争条件是当两个或多个程序(或单个程序的独立部分)都试图同时获取某些资源时,会导致错误的答案或冲突。此资源可以是信息,如下一个可用的约会时间,也可以是对电子表格等内容的独占访问权限。如果您曾经使用Microsoft Excel编辑共享驱动器上的文档,那么您可能已经体验过Excel告诉其他人已在编辑电子表格。此错误消息是Excel以优雅方式处理潜在竞争条件并防止错误的方式。

程序的一个常见任务是识别某种下一个可用值,然后分配它。这种技术用于发票号,学生证等。这是以前解决过的一个老问题。最常见的解决方案之一是允许存储数据的数据库生成数字。还有其他解决方案,它们都有自己的优点和缺点。

不幸的是,那些对这个领域一无所知或者对编程不好的程序员经常试图自己动手。聪明的人很快发现这是一个比看起来更复杂的问题,并寻找现有的解决方案。坏人从来没有看到问题,或者一旦他们这样做,坚持让他们不可行的解决方案变得更加复杂而不修复错误。我们以学生证为例。新手程序员说:“要知道下一个学生号码应该是什么,我们只需要获得最后一个学号并增加它。”这就是幕后发生的事情:

  1. 贝蒂,一名管理员。招生办公室的助理启动了学生管理计划。请注意,这实际上只是在她的PC上运行的程序的副本。它通过学校的网络与数据库服务器通信,但无法与其他PC上运行的程序的其他副本通信。
  2. 贝蒂为鲍勃史密斯创建了一个新的学生记录,输入了所有信息。
  3. 当Betty正在进行她的数据录入时,另一位管理员George。助理,在他的电脑上启动学生管理程序,并开始创建Gina Verde的记录。
  4. 乔治是一个更快的打字员,所以他和贝蒂同时完成。他们同时点击了“保存”按钮。
  5. Betty的程序连接到数据库服务器并获得正在使用的最高学号,5012。
  6. 乔治的节目同时得到同一个问题的答案。
  7. 两个程序都决定他们保存的记录的新学生ID应为5013.他们将该信息添加到记录中,然后将其保存在数据库中。
  8. 现在Bob Smith(Betty的学生)和Gina Verde(George的学生)拥有相同的学生证。
  9. 此学生证将附在各种其他记录中,从成绩到餐厅的餐卡。最终会出现这个问题,有人将不得不花费大量时间为其中一个人分配一个新ID并整理混合记录。

    当我向人们描述这个问题时,通常的反应是“但这在实践中会发生多少次?从不,对吧?“错误。首先,当您的员工完成数据输入时,通常在相对较短的时间内由每个人完成。这增加了重叠的可能性。如果有问题的应用程序是向公众开放的Web应用程序,则两个人同时点击“保存”按钮的可能性更高。我最近在生产系统中看到了这一点。这是一个公共测试版的Web应用程序。使用率非常低,每天只有少数人注册。然而,六对人设法在几个月的时间内获得相同的身份证。万一你想知道,不,我和我的团队中的任何人都没有写过这段代码。然而,我们对这个问题发生了多少次感到非常惊讶。事后看来,我们不应该这样。这实际上是墨菲定律的一个简单应用。

    如何避免这个问题?最简单的方法是使用现有的解决方案来解决经过充分测试的问题。所有主要数据库(MS SQL Server,Oracle,MySQL,PostgreSQL等)都有一种增加数字而不创建重复数据的方法。 MS SQL服务器将其称为“标识”列,而MySQL将其称为“自动编号”列,但功能相同。无论何时插入新记录,都会自动创建新标识符并保证其唯一性。这将改变上述情况如下:

    1. 贝蒂,一名管理员。招生办公室的助理启动了学生管理计划。请注意,这实际上只是在她的PC上运行的程序的副本。它通过学校的网络与数据库服务器通信,但无法与其他PC上运行的程序的其他副本通信。
    2. 贝蒂为鲍勃史密斯创建了一个新的学生记录,输入了所有信息。
    3. 当Betty正在进行她的数据录入时,另一位管理员George。助理,在他的电脑上启动学生管理程序,并开始创建Gina Verde的记录。
    4. 乔治是一个更快的打字员,所以他和贝蒂同时完成。他们同时点击了“保存”按钮。
    5. Betty的程序连接到数据库服务器并将其保存到记录中。
    6. 乔治的节目同时交出了另一张要保存的记录。
    7. 数据库服务器将两个记录放入队列并一次保存一个,为其分配下一个可用号码。
    8. 现在Bob Smith(Betty的学生)得到ID 5013,Gina Verde(George的学生)得到ID 5014。
    9. 使用此解决方案,重复没有问题。多年来,制造商和用户都反复测试了为每个数据库服务器执行此操作的代码。全球数以百万计的应用程序依赖它并继续每天对其进行压力测试。谁能说出他们自己开发的解决方案呢?

      至少有一种经过充分测试的方法可以在软件中而不是在数据库中创建标识符:uuids(通用唯一标识符)。但是,uuid采用xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx的形式,其中“x”代表十六进制数字(0-9和a-f)。您是否想将其用于发票编号,学生证或公众看到的其他标识符?可能不是。

      总而言之,当两个程序或程序的两个独立部分试图同时访问某些信息或访问资源时,会发生竞争条件,从而导致错误,不正确的计算,重复的标识符或者对资源的冲突。竞争条件比我在这里介绍的要多得多,它们会影响软件和硬件的许多其他领域。

答案 3 :(得分:-1)

您的问题的描述正是为什么主键可以是复合的,例如,它们由多个字段组成。这样,数据库将为您处理唯一性,您无需关心它。

在您的情况下,表定义可能类似于以下内容:

 CREATE TABLE `real_estate` (
   `id` int(11) NOT NULL AUTO_INCREMENT,
   `property` varchar(255) DEFAULT NULL,
   `property_value` varchar(255) DEFAULT NULL,
   PRIMARY KEY (`id`),
   UNIQUE KEY `index_id_property_property_value` (`id`, `property`, `property_value`),
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;