我应该通过数据库错误强制执行业务逻辑吗?

时间:2011-02-18 15:10:21

标签: database referential-integrity

最近我一直在考虑一个有趣的设计决定。假设我正在向表中添加用户名,我想确保没有重复项。用户名列为NOT NULL UNIQUE。我可以:

  1. 在插入之前查询数据库以确保没有重复的名称,或
  2. 只需INSERT,并捕获来自数据库引擎的任何异常。
  3. 假设我使用的数据库能够执行约束,我想知道每种选择适合的情况。

3 个答案:

答案 0 :(得分:1)

执行选项2似乎总是一个好主意。我不建议选项1,因为你有效地将插入所需的时间加倍(它们都需要先读取)。此外,一些新的开发人员将在某个时候提交,而不是支票,它将被破坏。

另一件需要考虑的事情是停机时间是多少?这是一个关键任务应用吗?如果业务逻辑腐败会发生什么?工厂会关闭吗?或者它只是一些恼人的错误。

你不能让你的工厂关闭,因为你没想到的一些例外使你的服务器崩溃了。因此,在这种情况下,对数据正确性进行夜间或每周检查也可能有所帮助。但是,我认为强制执行唯一性(以及可能的其他强制执行)的数据库功能是适当的方法。

答案 1 :(得分:1)

您是否可以缓存用户名列表并在应用程序端进行检查而无需访问数据库?您仍然应该对数据库具有唯一约束以确保没有错误数据进入(始终首先保护数据库级别的数据)但是如果您可以从缓存中进行检查,则可以保存整个往返的数据。有人选择与现有用户相同的用户名时的数据库。现在,这可能取决于您需要缓存的数据大小以及缓存必须更新的频率。不知道你的系统,我不能说它是否实用,但我至少会考虑这样做。

答案 2 :(得分:0)

您是否希望新用户名可能是唯一的?或者它可能是重复的?如果用户名可能是唯一的,那么执行插入和捕获异常会更有效。如果用户名可能是重复的,那么检查重复项(并且可能寻找类似但尚未采用的用户名)而不是尝试捕获异常将更有效。显然,不同的数据库和这些数据库的不同版本在相对概率上具有不同的盈亏平衡点。但总的来说,如果您正在为每个人都拥有唯一用户名的公司构建系统,请执行插入并捕获异常。如果您正在构建Hotmail,请先检查重复项。

快速演示(在Oracle 11.2.0.1上)显示,执行插入失败和处理异常的成本大约是在插入之前进行检查然后写入数据的大约7倍。

SQL> create table username_test (
  2    username varchar2(30) unique
  3  );

Table created.

SQL> set timing on;

SQL> ed
Wrote file afiedt.buf

  1  declare
  2    l_cnt integer;
  3  begin
  4    for i in 1 .. 100000
  5    loop
  6      select count(*)
  7        into l_cnt
  8        from username_test
  9       where username = 'JCAVE';
 10      if( l_cnt = 0 )
 11      then
 12        insert into username_test( username )
 13          values( 'JCAVE' );
 14      end if;
 15    end loop;
 16* end;
SQL> /

PL/SQL procedure successfully completed.

Elapsed: 00:00:04.20
SQL> rollback;

Rollback complete.

Elapsed: 00:00:00.00

SQL> ed
Wrote file afiedt.buf

  1  declare
  2    l_cnt integer;
  3  begin
  4    for i in 1 .. 100000
  5    loop
  6      begin
  7       insert into username_test( username )
  8          values( 'JCAVE' );
  9      exception
 10        when dup_val_on_index then
 11          null;
 12      end;
 13    end loop;
 14* end;
SQL> /

PL/SQL procedure successfully completed.

Elapsed: 00:00:29.58