Подтвердить что ты не робот

PHP - обнаружение ошибки/вставки mysql из-за нарушения уникального ограничения

Это похоже на этот вопрос:

PHP MySQL INSERT терпит неудачу из-за уникального ограничения

но у меня другой поворот. Скажем, у меня есть таблица с одним столбцом. Имя столбца - "title", и на нем есть уникальное ограничение.

Сначала я вставляю строку, где title= "something". В следующий раз, когда я попытаюсь вставить "что-то", он будет терпеть неудачу из-за уникального ограничения ключа (что хорошо). То, что я хотел бы сделать, это позволить ему выйти из строя и проверить код ошибки, предоставленный mysql, чтобы убедиться, что он не удалось из-за уникального ограничения ключа. (то есть, пусть база данных обрабатывает уникальность, и я просто обрабатываю код ошибки и сообщаю пользователю, что заголовок уже существует, когда возвращается результат).

Есть ли способ сделать это?

4b9b3361

Ответ 1

http://dev.mysql.com/doc/refman/5.5/en/error-messages-server.html

http://php.net/manual/en/function.mysql-errno.php

Мне приходилось делать это в прошлом, и это не весело:

if( mysql_errno() == 1062) {
    // Duplicate key
} else {
    // ZOMGFAILURE
}

Заметка о стиле программирования (Кредиты jensgram из this ответ)
Вы всегда должны избегать использования магических чисел. Вместо этого вы можете присвоить известному коду ошибки (1062) константе (например, MYSQL_CODE_DUPLICATE_KEY). Это упростит работу вашего кода, поскольку условие в инструкции if все еще доступно для чтения за несколько месяцев, когда значение 1062 исчезло из памяти:)

Ответ 2

Теперь, когда в 2015 году существует очень мало причин не использовать реализацию PHP PDO:

http://php.net/manual/en/book.pdo.php

Правильный, современный метод "OO" для обнаружения и обработки отказа вставки из-за нарушения ограничения ключа выглядит следующим образом:

try {
    //PDO query execution goes here.
}
catch (\PDOException $e) {
    if ($e->errorInfo[1] == 1062) {
        //The INSERT query failed due to a key constraint violation.
    }
}

В объекте PDOException также много говорится о специфике ошибки (более подробно, чем можно было бы когда-либо хотеть или нужно, по-видимому).

http://php.net/PDOException

Ответ 3

Я считаю, что код ошибки для дубликатов ключей - 1586. Если вы попытались выполнить запрос, а затем, при сбое, проверьте код ошибки, используя mysql_errno()/mysqli:: errno() и сравните его с 1586, который должен это сделать. Если это не 1586, проверьте, что это на самом деле, повторив код ошибки после вашего запроса.

Ответ 4

Почему бы просто не выбрать сначала, чтобы увидеть, существует ли запись уже. Или полностью устраните ошибку, используя INSERT ON DUPLCATE KEY UPDATE, или даже используйте ключевое слово IGNORE mysql. Почему преднамеренно вызывают ошибку?

Ответ 5

Тема интересна для пользователей PHP/Mysql, поэтому позвольте мне изложить решение. Обратите внимание

    • Нет волшебного портативного способа сделать это
    • ситуация не уникальна для PHP, если вы хотите обнаружить нарушение уникального ключа DB2 с помощью openJPA - вам нужно восстановить аналогичную обработку

Предположим, у вас есть форма - где у вас есть поле "Имя"

1) В таблице DB

Добавьте уникальное ограничение, например -

alter table wb_org add constraint uniq_name unique(name);

2) Обработчик формы script

Обработчик формы script должен передать данные на уровень БД, и если есть какие-либо ошибки, уровень БД будет сигнализировать об этом как о DBException (Исключение определено нами). мы переносим код, отправляющий данные на уровень БД в блок try-catch (отображается только соответствующий код)

try{

        .... 
        $organizationDao = new \com\indigloo\wb\dao\Organization();
        $orgId = $organizationDao->create($loginId,$fvalues["name"]) ;
        ....

    } catch(UIException $ex) {

       ....
       // do UI exception handling

    } catch(DBException $ex) {

        $errors = array();
        $code = $ex->getCode();
        $message = $ex->getMessage();

        // look for code 23000, our constraint name and keyword duplicate 
        // in error message thrown by the DB layer
        // Util::icontains is just case-insensitive stripos wrapper

        if( ($code == 23000)
            && Util::icontains($message,"duplicate")
            && Util::icontains($message,"uniq_name")) {
                $errors = array("This name already exists!");
        } else {
            // Not sure? show generic error
            $errors = array(" Error: doing database operation!") ;
        }

        // log errors
        Logger::getInstance()->error($ex->getMessage());
        Logger::getInstance()->backtrace($ex->getTrace());

        // store data in session to be shown on form page
        $gWeb->store(Constants::STICKY_MAP, $fvalues);
        $gWeb->store(Constants::FORM_ERRORS,$errors);

        // go back to form 
        $fwd = base64_decode($fUrl);
        header("Location: " . $fwd);
        exit(1);


    }catch(\Exception $ex) {

       // do generic error handling
    }

Обратите внимание, что вам нужно найти ex- > getCode() для вашей ситуации. Как и выше, уровень PDO фактически отбрасывает SQLSTATE 23000 как ex- > code (где фактический код ошибки mysql равен 1062). Код также может варьироваться от БД к БД. Такое же сообщение ex- > может также меняться. Было бы лучше обернуть эту проверку в одном месте и скриптировать с помощью файла конфигурации.

3) внутри уровня БД (с использованием PDO)

 static function create($loginId, $name) {
            $dbh = NULL ;

            try {

                $dbh =  PDOWrapper::getHandle();
                //Tx start
                $dbh->beginTransaction();
               ...
               // do DB operations
               //Tx end
                $dbh->commit();
                $dbh = null;

            } catch(\Exception $ex) {
                $dbh->rollBack();
                $dbh = null;
                throw new DBException($ex->getMessage(),$ex->getCode());
            }

4) Вернемся к форме (после удара формы Handler = > DB Layer = > Обработчик ошибок обработчика формы = > Форма)

Извлечь сообщения об ошибках, установленные в сеансе, и отобразить их в форме.

5) Класс DBException

<?php

namespace com\indigloo\exception {

    class DBException extends \Exception  {

        public function __construct($message,$code=0, \Exception $previous = null) {
            // PDO exception etc. can return strange string codes
            // Exception expects an integer error code.
            settype($code,"integer");
            parent::__construct($message,$code,$previous);
        }

    }
}

?>

6) iconetws метод утилиты

 static function icontains($haystack, $needle) {
        return stripos($haystack, $needle) !== false;
    }

Можем ли мы сделать это без исключений и PDO?

7) без PDO и используя только mysqli

Получить код ошибки и сообщение об ошибке из mysqli и выбросить DBException из уровня DB Обработчик DBException так же.

8) Можем ли мы это сделать без использования исключений?

Я пишу это без какого-либо опыта работы с ним в реальном коде. Поэтому, пожалуйста, дайте мне знать, если вы не согласитесь. Также, пожалуйста, поделитесь, если у вас есть лучшая схема. если вы просто хотите, чтобы у вас был общий способ сортировки, то да.

  • внутри уровня БД: вызывать ошибки с помощью trigger_error вместо исключения исключений. внутри метода trigger_error - используйте некоторые MAGIC_STRING + DB_CODE
  • определить собственный обработчик ошибок для страницы обработчика форм
  • внутри вашего пользовательского error_handler для обработчика формы: найдите MAGIC_STRING + code
  • если вы получите код MAGIC_STRING +, тогда
    • установить соответствующее сообщение в сеансе
    • переслать страницу
    • отображает пользовательский набор сообщений в сеансе

Проблема с find_error и error_handlers заключается в том, что

  • вы не можете ловить их в поток исполнения, как вы можете делать с исключениями. Однако это не проблема в нашем случае, потому что наш error_handler для страницы просто нужно перенаправить на страницу формы.
  • Я не знаю способ поднять конкретные коды ошибок (какой код мне нужен) с помощью метода trigger_error. Если бы можно было сделать ошибку с кодом X и нашим сообщением Y. Насколько я знаю, вы не можете этого сделать. Вот почему мы восстанавливаем разбор каждого error_message, который получает наш error_handler.

У меня нет большого опыта работы с кодами ошибок (я был поднят на исключениях) - так, может быть, кто-то еще может нас просветить.

Образцы кода из моего общедоступного github repo https://github.com/rjha/website - код, который я пишу, для создания создателя веб-сайта для запуска тысяч сайтов из того же БД. Приведенный выше код используется для проверки уникального имени для веб-сайта.

Ответ 6

Из Документация по PHP по функции mysql_errno:

Returns the error number from the last MySQL function, 
or 0 (zero) if no error occurred. 

Кроме того, из Документация по MySQL об ошибках нарушения Constraint, код ошибки 893 соответствует:

Constraint violation e.g. duplicate value in unique index

Итак, мы можем написать что-то подобное, чтобы выполнить работу:

if (!$result) {
    $error_code = mysql_errno();
    if ($error_code == 893) {
        // Duplicate Key
    } else {
        // Some other error.
    }
}

Ответ 7

Если вы знаете какой-нибудь SQL, попробуйте это решение (протестировано)

$username = "John";
$stmt = $pdo->prepare("
    INSERT INTO users (
        username
    ) SELECT * FROM (
        SELECT :username
    ) AS compare
    WHERE NOT EXISTS (
        SELECT username 
        FROM users 
        WHERE username = :username
    ) LIMIT 1;
");
$stmt->bindParam(":username", $username);
if ($stmt->execute()) {
    if ($stmt->rowCount() == 0) {
        echo "Dublicate Username, ".$username." already exists.";
    } else {
        echo $username." not in use yet.";
    }
}