check for duplicate entry vs use PDO errorInfo result

前端 未结 4 1596
被撕碎了的回忆
被撕碎了的回忆 2020-12-24 07:12

I have a MySQL table which has a field for email addresses which is defined as unique. For this example, let\'s say that all my form does is allow a user to insert their em

相关标签:
4条回答
  • 2020-12-24 07:32

    A simpler way that works for me is to check the error code against an array of duplicate status codes.That way you dont have to worry what PDO returns.

    $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
       }
    }
    

    Thanks! :-)

    0 讨论(0)
  • INSERT + check status should be a better approach. With SELECT + INSERT you can have another thread insert the same value between the SELECT and the INSERT, which means you would need to also wrap those two statements in a table lock.

    It is easy to err on the side of too much defense in your coding. Python has the saying that "it is easier to ask for forgiveness than to ask for permission", and this philosophy is not really Python-specific.

    0 讨论(0)
  • 2020-12-24 07:48

    Be aware that if you have an AUTO_INCREMENT column and you are using InnoDB, then a failed INSERT does increment the "next auto-id" value, despite no new row being added. See for example the documentation for INSERT ... ON DUPLICATE KEY and AUTO_INCREMENT Handling in InnoDB. This leads to gaps in AUTO_INCREMENT ids, which may be a concern if you think you might run out of ids.

    So if you expect that attempting to insert an already existing row is common, and you want to avoid gaps in AUTO_INCREMENT ids as much as possible, you can do both pre-emptive checking and exception handling:

    $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;
            }
        }
    }
    

    The first query ensures that we do not attempt to insert the email if we know for sure that it already exists. The second query attempts to insert the email if it seems not to exist yet. We still need to check for exceptions in the second query since a duplicate may still occur in the unlikely case of a race condition (multiple clients or threads running the snippet above in parallel).

    This approach makes the code robust, while still avoiding to create gaps in AUTO_INCREMENT ids except in the rare case of race conditions. It is also the fastest approach if attempting to insert an existing email is more common than attempting to insert an new email. If attempting to insert an existing email is rare, and you don't care about gaps in AUTO_INCREMENT ids, then there is no need for the pre-emptive check.

    0 讨论(0)
  • 2020-12-24 07:51

    You could be executing this with a try catch block:

    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
       }
    }
    

    You could also look into alternatives such as "INSERT IGNORE", and "INSERT... ON DUPLICATE KEY UPDATE" - though I think those are MySQL specific and would go against the portability of using PDO, if that's something you're concerned about.

    Edit: To more formally answer your question, to me, solution #1 (the defensive programmer) in full usage effectively eliminates the point of the unique constraint in the first place. So I would agree with your thought of letting MySQL take care of data checking.

    0 讨论(0)
提交回复
热议问题