我有一个MySQL表,其中有一个定义为唯一的电子邮件地址字段。对于此示例,假设我的所有表单仅允许用户将其电子邮件地址插入表中。
由于电子邮件字段是唯一的,因此如果他们尝试两次输入相同的电子邮件,则查询将失败。我对两种情况之间的权衡感到好奇:
1)SELECT
在执行插入操作之前先运行一个快速语句。如果select返回结果,则通知用户,不要运行该INSERT
语句。
2)运行该INSERT
语句,并检查重复的输入错误
// snippet uses PDO
if (!$prep->execute($values))
{
$err = $prep->errorInfo();
if (isset($err[1]))
{
// 1062 - Duplicate entry
if ($err[1] == 1062)
echo 'This email already exists.';
}
}
另外,请假定正常使用,这意味着重复的条目应该最少。因此,在第一种情况下,您显然要为每个插入都运行一个附加查询,而在第二种情况下,则依靠错误处理。
另外,我很好奇听到有关编码风格的想法。我的心说:“成为一名防御性程序员!在插入之前检查数据!我的大脑说:“嗯,也许最好让MySQL为您检查数据”。
编辑-请注意,这不是一个“我该怎么做”问题,而是一个“为什么我应该以特定方式做这个”问题。我包括的小代码段是有效的,但是我很好奇的是解决问题的最佳方法。
INSERT
+检查状态应该是更好的方法。使用SELECT
+,INSERT
您可以让另一个线程在SELECT
和之间插入相同的值INSERT
,这意味着您还需要将这两个语句包装在表锁中。
在编码中过多的防御很容易犯错。Python有句俗语:“寻求宽容比寻求许可要容易得多”,而这种哲学并不是真正针对Python的。
您可以使用try catch块执行此操作:
try {
$prep->execute($values);
// do other things if successfully inserted
} catch (PDOException $e) {
if ($e->errorInfo[1] == 1062) {
// duplicate entry, do something else
} else {
// an error other than duplicate entry occurred
}
}
您还可以研究“ INSERT IGNORE”和“ INSERT ... ON DUPLICATE KEY UPDATE”之类的替代方法-尽管我认为这些特定于MySQL,如果您担心的是PDO的可移植性, 。
编辑:为了更正式地回答您的问题,对我来说,全面使用的解决方案#1(防御性程序员)首先消除了唯一约束的问题。因此,我同意您的想法,让MySQL负责数据检查。
请注意,如果您有一个AUTO_INCREMENT列并且正在使用InnoDB,那么即使没有添加任何新行,失败的INSERT也会增加“下一个自动ID”值。例如,请参阅InnoDB中有关INSERT ... ON DUPLICATE KEY和AUTO_INCREMENT处理的文档。这会导致AUTO_INCREMENT ID出现空白,如果您认为ID可能用完了,则可能要担心。
因此,如果您希望尝试插入一个已经存在的行很常见,并且希望尽可能避免在AUTO_INCREMENT ID中出现空白,则可以同时进行先占检查和异常处理:
$already_exists = false;
$stmt = $pdo->prepare("SELECT id FROM emails WHERE email = :email");
$stmt->execute(array(':email' => $email));
if ($stmt->rowCount() > 0) {
$already_exists = true;
}
else {
try {
$stmt = $pdo->prepare("INSERT INTO emails (email) VALUES (:email)");
$stmt->execute(array(':email' => $email));
} catch (PDOException $e) {
if ($e->errorInfo[1] == 1062) {
$already_exists = true;
} else {
throw $e;
}
}
}
第一个查询可确保我们在确定已经存在的情况下不尝试插入电子邮件。第二个查询尝试插入似乎还不存在的电子邮件。我们仍然需要在第二个查询中检查异常,因为在不太可能的竞争情况下(多个客户端或并行运行上述代码片段的线程)可能仍然会发生重复。
这种方法使代码健壮,同时除了在极少数的竞争条件下,仍避免在AUTO_INCREMENT id中创建间隙。如果尝试插入现有电子邮件比尝试插入新电子邮件更常见,这也是最快的方法。如果尝试插入现有电子邮件的情况很少,并且您不关心AUTO_INCREMENT ID的间隔,则无需进行先发制人检查。
对我来说,一种更简单的方法是根据一系列重复的状态代码检查错误代码,这样您就不必担心PDO返回什么。
$MYSQL_DUPLICATE_CODES=array(1062,23000);
try {
$prep->execute($values);
// do other things if successfully inserted
} catch (PDOException $e) {
if (in_array($e->getCode(),$MYSQL_DUPLICATE_CODES)) {
// duplicate entry, do something else
} else {
// an error other than duplicate entry occurred
}
}
谢谢!:-)
文章标签:mysql , pdo , php
版权声明:本文为原创文章,版权归 admin 所有,欢迎分享本文,转载请保留出处!
评论已关闭!