WPDB вставка или обновление, если существует
Я не слишком хорошо знаком с WPDB или SQL в целом, но у меня есть пользовательская таблица для моего проекта, и я пытаюсь присвоить ей некоторые метаданные. Что бы я "хотел", чтобы произошло: если строка существует - обновить её, если нет - вставить новую. Я прочитал про Insert и Update в документации WPDB, но ни один из них не рассматривал ситуацию "либо то, либо другое". Я думал, что смогу работать с update, поэтому мой код пока выглядит так:
// Обновление записи в таблице
$wpdb->update(
$wpdb->prepare(
$wpdb->prefix.'item_info',
array(
'post_id' => $post_id,
'item_stock' => $item_stock
),
array('post_id' => $post_id)
)
);
Есть ли в WordPress что-то вроде "ЕСЛИ существует - обновить, ИНАЧЕ - вставить", или мне нужно выполнять пользовательский SQL-запрос для достижения этого, или мне нужно сначала сделать запрос к базе данных, чтобы проверить, существует ли ID в моей таблице, а ЗАТЕМ решить, обновлять его или вставлять?

Вы пробовали использовать $wpdb->replace
? Согласно WP Codex:
Заменяет строку в таблице, если она существует, или вставляет новую строку, если такой строки еще нет.
Я сам использовал этот метод в нескольких плагинах, и он отлично справляется с предотвращением ошибок дублирования уникальных ID и подобных проблем.

Это сработало для меня, в то время как пользовательский запрос не сработал — спасибо за упоминание replace()

стоит отметить, что $wpdb->replace
полностью перезаписывает всю запись, тогда как $wpdb->update
обновляет только указанные поля из массива $data

Эта функция сейчас не выполняет замену для меня. Она дублирует одни и те же строки. Может ли PRIMARY KEY (id) быть причиной, которая препятствует замене? Я не могу найти информацию об этом в Codex.

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

Во-первых, вы используете prepare
неправильно. Похоже, что вы обернули аргументы $wpdb->update
в $wpdb->prepare
таким образом. Это не сработает. Фактически, вы передаёте в update
только один аргумент — результат выполнения prepare
. Попробуйте что-то простое, например, следующее, и вы поймёте, почему это не работает:
$post_id = 123;
$item_stock = 567;
var_dump(
$wpdb->prepare(
$wpdb->prefix.'item_info',
array(
'post_id' => $post_id,
'item_stock' => $item_stock
),
array('post_id' => $post_id)
)
);
Кроме того, $wpdb->update()
сам выполняет prepare
за вас.
Во-вторых, если бы это был я, я бы избегал излишней сложности с вспомогательными функциями и написал бы правильный запрос ON DUPLICATE KEY UPDATE
:
$sql = "INSERT INTO {$wpdb->prefix}item_info (post_id,item_stock) VALUES (%d,%s) ON DUPLICATE KEY UPDATE item_stock = %s";
// var_dump($sql); // отладка
$sql = $wpdb->prepare($sql,$post_id,$item_stock,$item_stock);
// var_dump($sql); // отладка
$wpdb->query($sql);
Это предполагает, что post_id
является UNIQUE
индексом или PRIMARY KEY
. Если структура вашей таблицы соответствует моим предположениям, позвольте базе данных разобраться с этим.

Метод prepare возвращает false для меня - других ошибок БД нет. Если выполнить запрос вручную через phpmyadmin, все работает как ожидалось. Также проверил, что переменные содержат правильные значения.. Есть идеи?

Вы можете попробовать обновить таблицу с помощью $wpdb->update
. В следующем коде я использую id, но вы можете использовать любые критерии.
$result = $wpdb->update($tableName, $info, array('id' => $info["id"]));
// Если не найдено записей для обновления, попытается создать запись.
if ($result === FALSE || $result < 1) {
$wpdb->insert($tableName, $info);
}
Результат работы Update()
$result === FALSE
: Ошибка$result === 0
: Успешно, но ничего не обновлено$result > 1
: Успешно и обновлено
