Повторяющиеся записи в таблице wp_postmeta обычно всплывают не сразу: сайт работает, но со временем раздувается база, усложняется отладка и появляются странные эффекты в теме или плагинах. Чаще всего дубли создают импорты, кривые сохранения метабоксов, повторные вызовы update_post_meta() с массивами и плагины, которые пишут одно и то же значение в несколько проходов.
Ниже — рабочий сценарий: как сначала подтвердить, что проблема именно в дублях метаданных, потом безопасно их найти, удалить и проверить результат. Без «чистки на глаз» и без опасных массовых запросов в лоб.
Когда дубли метаданных действительно мешают
Не каждая повторяющаяся запись в postmeta — ошибка. WordPress допускает несколько значений одного ключа для одного поста, если код явно это использует. Проблема начинается, когда один и тот же ключ хранит одинаковое значение много раз, а код темы или плагина ожидает одну запись.
Типичные симптомы
- в админке медленно открываются записи с большим количеством метаполей;
- после импорта или синхронизации часть настроек «скачет» между одинаковыми значениями;
- в базе заметно растёт
wp_postmeta, хотя контента почти не прибавилось; - плагины фильтрации, SEO или кастомные поля начинают работать нестабильно;
- один и тот же ключ возвращается несколько раз через
get_post_meta()с третьим параметромfalse.
Диагностика: где именно появились дубли
Сначала нужно понять, это дубли по ключу, по значению или и то и другое. Для этого удобно посмотреть, какие пары post_id + meta_key встречаются чаще одного раза.
SELECT post_id, meta_key, COUNT(*) AS cnt
FROM wp_postmeta
GROUP BY post_id, meta_key
HAVING cnt > 1
ORDER BY cnt DESC, post_id ASC;Если нужно найти именно одинаковые значения у одного ключа, используйте более точный запрос:
SELECT post_id, meta_key, meta_value, COUNT(*) AS cnt
FROM wp_postmeta
GROUP BY post_id, meta_key, meta_value
HAVING cnt > 1
ORDER BY cnt DESC;Эти запросы лучше запускать сначала на копии базы или хотя бы в режиме только чтения. Если таблица большая, результат может быть тяжёлым, поэтому не стоит выполнять их в часы пик на живом сайте без необходимости.
Что проверить до удаления
- какие ключи повторяются чаще всего;
- использует ли код этот ключ как одиночное значение или как массив;
- есть ли у ключа сериализованные данные — их нельзя сравнивать и чистить «по частям»;
- не создаёт ли дубли сам плагин при каждом сохранении записи;
- есть ли резервная копия базы перед любыми изменениями.
Как удалить дубли безопасно
Самый надёжный путь — сначала определить, какие записи оставить, а какие удалить. Для простых ключей с одинаковым значением можно оставить запись с минимальным meta_id, а остальные удалить. Но делать это нужно только после проверки, что ключ не хранит множественные значения намеренно.
Вариант через SQL
Ниже пример для случая, когда нужно убрать одинаковые дубли по одной паре post_id + meta_key + meta_value, оставив первую запись:
DELETE pm1
FROM wp_postmeta pm1
INNER JOIN wp_postmeta pm2
ON pm1.post_id = pm2.post_id
AND pm1.meta_key = pm2.meta_key
AND pm1.meta_value = pm2.meta_value
AND pm1.meta_id > pm2.meta_id;Этот запрос удаляет только повторяющиеся строки, у которых полностью совпадают ключ и значение. Но он не подходит для сериализованных данных и ключей, где дубли допустимы по логике плагина.
Вариант через WP-CLI и PHP
Если нужен более контролируемый сценарий, удобнее написать короткий скрипт и выполнить его через wp eval-file. Так проще добавить фильтрацию по конкретным ключам.
<?php
// cleanup-postmeta-duplicates.php
$keys = [
'_my_custom_flag',
'_my_custom_text',
];
global $wpdb;
foreach ($keys as $key) {
$rows = $wpdb->get_results(
$wpdb->prepare(
"SELECT post_id, meta_value, GROUP_CONCAT(meta_id ORDER BY meta_id) AS ids, COUNT(*) AS cnt
FROM {$wpdb->postmeta}
WHERE meta_key = %s
GROUP BY post_id, meta_value
HAVING cnt > 1",
$key
)
);
foreach ($rows as $row) {
$ids = array_map('intval', explode(',', $row->ids));
array_shift($ids); // оставляем первую запись
foreach ($ids as $meta_id) {
$wpdb->delete($wpdb->postmeta, ['meta_id' => $meta_id], ['%d']);
}
}
}Запуск:
wp eval-file cleanup-postmeta-duplicates.phpПеред запуском такого скрипта стоит ограничить список ключей только теми, в которых вы уверены. Это не универсальная «уборка всего подряд».
Сравнение подходов: SQL, WP-CLI или плагин
| Подход | Когда подходит | Риск | Комментарий |
|---|---|---|---|
| SQL-запрос | Нужно быстро убрать точные дубли | Средний | Подходит для опытного админа и понятной структуры данных |
| WP-CLI + PHP | Нужна фильтрация по ключам и контроль логики | Низкий-средний | Лучше для точечной очистки и повторяемых задач |
| Плагин очистки базы | Нужен интерфейс и минимум ручной работы | Зависит от плагина | Проверяйте, умеет ли он работать именно с postmeta, а не только с ревизиями и transient |
Если вам нужен не только разовый разбор дублей, но и регулярная техническая чистка сайта, имеет смысл смотреть в сторону инструментов, которые умеют работать с дублями, метаданными и служебными записями без лишней магии. Например, у Clearfy Pro есть набор функций для технической оптимизации и удаления части мусора, но перед использованием всё равно нужно понимать, что именно он меняет в вашей базе.
Проверка результата после удаления
После очистки не ограничивайтесь тем, что «ошибок не было». Проверьте данные и поведение сайта.
Что проверить в базе
- повторно запустите запрос на дубли и убедитесь, что нужные пары исчезли;
- сравните количество строк в
wp_postmetaдо и после; - посмотрите, не пропали ли значения у записей, где метаполе должно быть уникальным;
- если использовали SQL, проверьте, не осталось ли «сиротских» ключей без нужных значений.
Что проверить в интерфейсе
- открытие и сохранение записи в админке;
- отображение полей на фронтенде;
- работу фильтров, которые завязаны на эти метаданные;
- поиск по сайту, если метаполя участвуют в построении выдачи.
Если после очистки что-то сломалось, первым делом восстановите базу из бэкапа и проверьте, не удалили ли вы ключ, который должен хранить несколько значений.
Частые ошибки и как их исправить
Удаляют все строки с одинаковым ключом
Это самая опасная ошибка. Один и тот же meta_key может использоваться для массива значений. Удаление по одному ключу без учёта meta_value почти гарантированно ломает данные.
Не учитывают сериализованные значения
Если в meta_value лежит сериализованный массив, сравнение по частям бессмысленно. Такие записи нужно обрабатывать только после точного понимания структуры данных.
Чистят без резервной копии
Даже аккуратный запрос может удалить нужную запись, если логика плагина нестандартная. Перед изменениями нужен бэкап базы и, желательно, тестовая копия сайта.
Путают дубли с ревизиями и автосохранениями
Иногда проблема не в postmeta, а в том, что запись многократно сохранялась из-за ревизий или автосохранений. Тогда чистить нужно не метаданные, а источник повторных сохранений.
Как не допустить повторного появления дублей
Если дубли создаёт ваш код, исправлять нужно не базу, а логику сохранения. В метабоксах и обработчиках сохранения проверяйте nonce, права пользователя и не вызывайте update_post_meta() несколько раз подряд без необходимости.
add_action('save_post', function ($post_id) {
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
return;
}
if (!isset($_POST['my_meta_nonce']) || !wp_verify_nonce($_POST['my_meta_nonce'], 'my_meta_save')) {
return;
}
if (!current_user_can('edit_post', $post_id)) {
return;
}
$value = isset($_POST['my_custom_field']) ? sanitize_text_field(wp_unslash($_POST['my_custom_field'])) : '';
update_post_meta($post_id, '_my_custom_field', $value);
});Если плагин пишет дубли из-за повторных запросов, ищите место, где обработчик вызывается дважды: через save_post, отдельный AJAX-эндпоинт или импортный скрипт. В таких случаях помогает логирование входных данных и контроль уникальности перед записью.
Практические советы по безопасности и производительности
- не запускайте массовое удаление на боевом сайте без бэкапа;
- для больших таблиц сначала тестируйте запрос на выборке с
LIMITили на копии базы; - если чистите регулярно, фиксируйте список проблемных ключей отдельно;
- после удаления дублей проверьте, не нужен ли
ANALYZE TABLEдля актуализации статистики оптимизатору MySQL/MariaDB; - если база сильно разрослась, имеет смысл отдельно проверить неиспользуемые метаданные и служебные записи, а не только дубли.
В итоге задача сводится не к «удалить лишнее», а к тому, чтобы понять, какие метаданные должны быть уникальными, а какие — нет. Если сначала диагностировать источник дублей, потом чистить точечно и только после этого проверять поведение сайта, риск поломки становится заметно ниже.