Во второй части мы заменили запрос к базе с сортировкой на сортировку в PHP — так кэш запросов перестал мешать. Такая замена безопасна, только если порядок однозначен. Если у двух товаров одинаковое значение поля сортировки, их взаимный порядок выбирает база, и повторить его в PHP нельзя. На тесте таких пар не было ни одной. На рабочем сайте их было три.
Это третья часть цикла «Массовый импорт в WordPress».
Откуда ничьи
Порядок товаров задаёт поле order, его присылает учётная система. Значения ставят люди, и совпадения неизбежны: два трактора с order = 225. Запрос orderby => meta_value_num отсортирует их рядом, но кто из двух первый — решает база, а не мы.
Тест и рабочий сайт расходятся именно здесь: на тестовой копии совпадений не оказалось ни одного. Проверка «на тесте всё совпало» ничего не говорит о ничьих — их надо искать запросом на рабочих данных.
SELECT meta_value, COUNT(*) AS n, GROUP_CONCAT(post_id) AS ids
FROM wp_postmeta pm JOIN wp_posts p ON p.ID = pm.post_id
WHERE pm.meta_key = 'order' AND p.post_type = 'katalog' AND p.post_status = 'publish'
GROUP BY meta_value HAVING n > 1;
Первое решение: при ничьей — по-старому
На первом сайте сделали осторожно: функция, строящая таблицу порядка, возвращает null, если в наборе есть ничья, и код идёт прежним медленным путём через get_posts. Ничьих было три пары на весь каталог, медленный путь срабатывал редко.
Когда ту же правку переносили на второй сайт, это решение обесценило всю работу. Там пара с одинаковым order попадала в каждое крупное объединение товаров: из 10 самых связных товаров все 10 шли по медленному пути.
| Самый связный товар | Время записи |
|---|---|
| без ничьей в наборе | 2,15 с |
| с ничьей — запасной путь | 16,83 с |
Прежнего поведения не существует
Запасной путь строился на желании «сохранить, как было». Проверка показала, что сохранять нечего. База возвращает равные значения в разном порядке в зависимости от набора include: у одного товара 12 адресатов из 101 получали эту пару в другом порядке, чем остальные. Один и тот же товар в разных списках стоял то выше, то ниже соседа.
Решение — сделать порядок однозначным вторым ключом сортировки:
'orderby' => ['meta_value_num' => 'ASC', 'ID' => 'ASC'],
И в PHP — то же правило: при равном order меньший ID идёт первым. Теперь база и PHP дают одинаковый порядок всегда, запасной путь не нужен. Если людям не понравится, какой из двух товаров первый, — это решается в учётной системе: проставить разные значения order.
Проверить, что ничья вообще на что-то влияет
На втором сайте нашлось четыре группы с одинаковым order, но три из них оказались безобидными: навесное оборудование, у которого списки совместимости хранятся на нём самом и содержат тракторы. На порядок внутри групп такие ничьи не влияли вовсе. Решающий вопрос не «есть ли ничья», а «попадает ли она в список, который мы строим».
Ещё одно отличие сайтов: данные, а не способ
У двух сайтов с общей родословной темы оказалась разная структура меты: на одном список хранится отсортированным и без общего названия, на другом — в исходном порядке и с ним. Ускорять надо способ получения данных, а что именно записывается, оставлять как было в проекте. Перенос «как на первом сайте» поменял бы данные на втором.
Переносите оптимизацию с одного сайта на другой?
Пришлите оба — сверю данные и найду места, где «одинаковый» код ведёт себя по-разному.
// загружаю комментарии…