如果用户至少查看过一次对象,我有一张表正在录制,因此:
HasViewed ObjectID number (FK to Object table) UserId number (FK to Users table)
两个字段都不是NULL,并且一起形成主键.
我的问题是,因为我不关心有人看过一个对象多少次(在第一次之后),我有两个处理插入的选项.
执行SELECT计数(*)...如果未找到任何记录,请插入新记录.
总是只插入一条记录,如果它抛出DUP_VAL_ON_INDEX异常(表明已有这样的记录),就忽略它.
选择第二种选择的缺点是什么?
更新:
我想最好的方法是:"异常引起的开销是否比初始选择引起的开销更糟?"
我通常只是插入并捕获DUP_VAL_ON_INDEX异常,因为这是最简单的代码.这比插入前检查存在更有效.我不认为这是一个"难闻的气味"(可怕的短语!),因为我们处理的异常是由Oracle引发的 - 它不像将自己的异常作为流控制机制提升.
感谢Igor的评论我现在运行了两个不同的benchamrks:(1)除了第一个之外的所有插入尝试都是重复的,(2)其中所有插入都不是重复的.现实将介于两种情况之间.
注意:在Oracle 10.2.0.3.0上执行的测试.
案例1:大多数重复
似乎最有效的方法(通过一个重要因素)是检查存在何时插入:
prompt 1) Check DUP_VAL_ON_INDEX begin for i in 1..1000 loop begin insert into hasviewed values(7782,20); exception when dup_val_on_index then null; end; end loop rollback; end; / prompt 2) Test if row exists before inserting declare dummy integer; begin for i in 1..1000 loop select count(*) into dummy from hasviewed where objectid=7782 and userid=20; if dummy = 0 then insert into hasviewed values(7782,20); end if; end loop; rollback; end; / prompt 3) Test if row exists while inserting begin for i in 1..1000 loop insert into hasviewed select 7782,20 from dual where not exists (select null from hasviewed where objectid=7782 and userid=20); end loop; rollback; end; /
结果(运行一次以避免解析开销):
1) Check DUP_VAL_ON_INDEX PL/SQL procedure successfully completed. Elapsed: 00:00:00.54 2) Test if row exists before inserting PL/SQL procedure successfully completed. Elapsed: 00:00:00.59 3) Test if row exists while inserting PL/SQL procedure successfully completed. Elapsed: 00:00:00.20
案例2:没有重复
prompt 1) Check DUP_VAL_ON_INDEX begin for i in 1..1000 loop begin insert into hasviewed values(7782,i); exception when dup_val_on_index then null; end; end loop rollback; end; / prompt 2) Test if row exists before inserting declare dummy integer; begin for i in 1..1000 loop select count(*) into dummy from hasviewed where objectid=7782 and userid=i; if dummy = 0 then insert into hasviewed values(7782,i); end if; end loop; rollback; end; / prompt 3) Test if row exists while inserting begin for i in 1..1000 loop insert into hasviewed select 7782,i from dual where not exists (select null from hasviewed where objectid=7782 and userid=i); end loop; rollback; end; /
结果:
1) Check DUP_VAL_ON_INDEX PL/SQL procedure successfully completed. Elapsed: 00:00:00.15 2) Test if row exists before inserting PL/SQL procedure successfully completed. Elapsed: 00:00:00.76 3) Test if row exists while inserting PL/SQL procedure successfully completed. Elapsed: 00:00:00.71
在这种情况下,DUP_VAL_ON_INDEX会赢一英里.请注意,"插入前选择"在两种情况下都是最慢的.
因此,根据插入是否重复的相对可能性,您似乎应该选择选项1或3.