在具有唯一列的数据库中插入时,可以使用try / catch而不是检查重复项吗?

时间:2018-08-05 18:18:06

标签: php mysql laravel innodb

所以我有一个具有唯一列的表

transactions : id , trace_code (unique) , amount , created_at 

我有一个机器人,它从银行api读取我的交易,并每隔x分钟使用cronjob将它们插入到我的数据库中……因此每次执行中都有很多重复的值

因此我可以在将数据插入该表之前检查重复项 但是只需插入try / catch块中就容易得多了……这意味着更少的代码和更少的查询

try {

 $tr = new Transaction();
 $tr->amount = 1000 ;
 $tr->trace_code = 123 ; 
 $tr->save();
}
catch(\Exception $e){

  echo " duplicate value";
}

所以我的问题是,当我知道会有很多重复的值时,这样做有什么缺点吗?

3 个答案:

答案 0 :(得分:1)

是的,这是正确的方法。否则,另一个请求可能会在您检查行是否存在之后但在设法进行插入之前插入一行。 (以防cron作业有2个实例同时运行)。

但是请确认您获得的异常确实来自唯一的索引异常,而不是其他失败的异常,因此您最终不会忽略其他错误。

答案 1 :(得分:1)

您可能对Eloquent的firstOrNew方法感兴趣。如果找不到匹配的记录,它将立即创建模型的新实例,而无需立即保存。然后,您可以决定相应地保存/更新:

// $tr will be the first matching record or a new instance
$tr = Transaction::firstOrNew([
    'amount' => 1000,
    'trace_code' => 123
]);

// do stuff....
$tr->save();

使用locks来防止其他查询更新行。

答案 2 :(得分:1)

我认为其他答案中有一个潜在的问题-id的燃烧。

假设您的表包含

id INT AUTO_INCREMENT PRIMARY KEY,
trace_code ... UNIQUE

然后...当您尝试插入重复的行(由于trace_code)并且陷阱(或为IGNOREd)时,id已经被碰到。

看看SELECT MAX(id) FROM tbl,看看是否发生了这种情况。

在某个时候,INT将溢出;这会造成麻烦。

我喜欢的解决方案是摆脱id并使trace_code成为PRIMARY KEY。然后INSERT IGNORE要么插入,要么静默不执行任何操作。不需要额外的检查,陷阱等。 (继续尝试try..catch,以防万一其他问题出了问题。“ catch”应该是例外,而不是“ norm”。)

还有其他解决方案,但更麻烦。