Подготовленный отчет с ключом ON DUPLICATE

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

if($stmt = $mysqli -> prepare("INSERT INTO user_info (city, state, website, public_contact, user, zipcode, pic, emailme) VALUES (?, ?, ?, ?, ?, ?, ?,?) 
    ON DUPLICATE KEY UPDATE (city, state, website, public_contact, user, zipcode, pic, emailme) VALUES (?, ?, ?, ?, ?, ?, ?,?)")) {
    $stmt -> bind_param("sssssssi",$city, $state, $website, $public_contact, $user, $zipcode, $pic, $emailme);
    $stmt -> execute();
    $stmt -> bind_result($result);
    $stmt -> close();
}

пользователь является уникальным. Эта ИМО - это просто синтаксическая проблема, поэтому кто-нибудь может помочь мне с правильным синтаксисом? Очень признателен.

ETA: просто для того, чтобы помочь устранить проблему, это работает по назначению, когда я удаляю часть ON DUPLICATE KEY UPDATE, но, очевидно, она разрешает только одну запись для каждого пользователя и не будет обновлять

UPDATE: никогда не мог найти рабочий синтаксис для использования UPDATE ON DUPLICATE KEY UPDATE, поэтому то, что я сделал (по общему мнению, возможно, не самый эффективный способ), проверял таблицу перед рукой для пользователя. Если пользователь существует, я запускаю и UPDATE, если нет, я запускаю INSERT. Ниже мой рабочий код. Надеюсь, это поможет кому-то, кто застрял в моей ситуации.

 $sql = "SELECT * FROM user_info WHERE user='$user'";

 if ($result=mysqli_query($mysqli,$sql))
 {
 /* Return the number of rows in result set */
 $rows=mysqli_num_rows($result);
 /* Free result set */
 mysqli_free_result($result);
 } 

 if($rows == 0) { 
if($stmt = $mysqli -> prepare("INSERT INTO user_info (city, state, website, public_contact, user, zipcode, pic, emailme) VALUES (?, ?, ?, ?, ?, ?, ?,?) ")) {

$stmt -> bind_param("sssssssi",$city, $state, $website, $public_contact, $user, $zipcode, $pic, $emailme);
$stmt -> execute();
$stmt -> bind_result($result);
$stmt -> close();
}

} else {

if($stmt = $mysqli -> prepare("UPDATE user_info SET city=?, state=?, website=?, public_contact=?, zipcode=?, pic=?, emailme=? WHERE user='$user'")) {

$stmt -> bind_param("ssssssi",$city, $state, $website, $public_contact, $zipcode, $pic, $emailme);
$stmt -> execute();
$stmt -> bind_result($result);
$stmt -> close();
}
    }

Ответ 1

Самый простой способ использовать INSERT... ON DUPLICATE KEY UPDATE - использовать предложение VALUES следующим образом, поэтому вам не нужно повторять параметры в предложении UPDATE. Они используют одни и те же значения для каждого столбца, который вы передали в предложении VALUES:

if($stmt = $mysqli -> prepare("
    INSERT INTO user_info (city, state, website, public_contact, 
        user, zipcode, pic, emailme)
    VALUES (?, ?, ?, ?, ?, ?, ?, ?) 
    ON DUPLICATE KEY UPDATE
        city = VALUES(city),
        state = VALUES(state),
        website = VALUES(website),
        public_contact = VALUES(public_contact),
        user = VALUES(user),
        zipcode = VALUES(zipcode),
        pic = VALUES(pic),
        emailme = VALUES(emailme)") {
    $stmt -> bind_param("sssssssi",$city, $state, $website, $public_contact, 
        $user, $zipcode, $pic, $emailme);
    $stmt -> execute();
    $stmt -> close();
}

Синтаксис IODKU требует, чтобы каждый столбец был установлен отдельно. Вы не можете перечислить их все в одном пункте, как вы пытались сделать.

Вы всегда должны сообщать о любых ошибках из любого вызова для подготовки() или execute(). Или вы можете сделать исключения mysqli throw:

$mysqli -> report_mode = MYSQLI_REPORT_STRICT;

Кроме того, вам не нужно bind_result(), так как нет набора результатов из INSERT:

// NO: $stmt -> bind_result($result);

Ответ 2

Я бы настоятельно предложил взглянуть на что-то вроде Doctrine DBAL (не ORM) - он позволяет вам перехватывать пары ключей = > значение и позволяет этим типам операций легче владеть, когда имеется так много значений.

Затем вы можете сделать что-то вроде:

try {
    $conn->insert(
    'db.`table`',
     [
       'city' => $city,
       'state' => $state
     ]);
} catch (Exception $e) {

    if( $e->getCode() !== '23000' ) {
        throw $e;
    }

    $conn->update(
    'db.`table`',
     [
       'city' => $city,
       'state' => $state
     ],
     [
       'user' => $user
     ]);
}