Ручной запрос сообщений по таксономии с использованием MySQL.

Вопрос или проблема

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

Вот мой запрос

SELECT * FROM wptests_posts wp
INNER JOIN wptests_postmeta wm ON (wm.`post_id` = wp.`ID`)
INNER JOIN wptests_term_relationships wtr ON (wp.`ID` = wtr.`object_id`)
INNER JOIN wptests_term_taxonomy wtt ON (wtr.`term_taxonomy_id` = wtt.`term_taxonomy_id`)
INNER JOIN wptests_terms wt ON (wt.`term_id` = wtt.`term_id`) AND
((wtt.`taxonomy` = 'category' AND  wt.`slug` = 'uncategorized'))

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

Любая помощь приветствуется

Это отлично сработало для меня. Включение postmeta может быть немного сложным. Без него я получил около 150 результатов. С ним — более 5000. Я никогда не получал пустой результат.

Поскольку postmeta может иметь от нуля до сотен элементов на запись, создается новая строка для каждого элемента postmeta с повторением информации о записи для каждой строки.

Поэтому я добавил GROUP BY wp.ID, и это сломалось:

Ошибка запроса 1: Выражение #24 в списке SELECT не включено в предложение GROUP BY
и содержит неагрегированный столбец ‘local.wm.meta_id’, который не
функционально зависит от столбцов в GROUP BY; это
несовместимо с sql_mode=only_full_group_by

Это было главным образом потому, что колонка GROUP BY (wp.ID) должна быть в SELECT. Это все равно не дало бы вам postmeta. Поэтому без group by это работало, вам просто нужно было бы обработать это после получения результатов и затем объединить их.

Нет отличного способа получить все мета данные в одном запросе. Лучше указать то, что вам нужно, или использовать более одного запроса.

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

SELECT
    wp.ID,
    wp.*,
    GROUP_CONCAT(wm.meta_key ORDER BY wm.meta_key DESC SEPARATOR '||') as meta_keys,
    GROUP_CONCAT(wm.meta_value ORDER BY wm.meta_key DESC SEPARATOR '||') as meta_values
FROM
    wptests_posts wp
    INNER JOIN wptests_postmeta wm ON (wm.`post_id` = wp.`ID`) AND wm.meta_key NOT LIKE '\_%'
    INNER JOIN wptests_term_relationships wtr ON (wp.`ID` = wtr.`object_id`)
    INNER JOIN wptests_term_taxonomy wtt ON (wtr.`term_taxonomy_id` = wtt.`term_taxonomy_id`) AND (wtt.`taxonomy` = 'category')
    INNER JOIN wptests_terms wt ON (wt.`term_id` = wtt.`term_id`) AND  (wt.`slug` = 'uncategorised')
    GROUP BY wp.ID

Что-то вроде:

function custom_separate_postmeta( $a ) {
    $output = array_merge( $a, array_combine( explode('||', $a['meta_keys']), array_map('maybe_unserialize', explode('||', $a['meta_values'])) ) );
    unset($output['meta_keys']);
    unset($output['meta_values']);
    return $output;
}

И вы можете запустить это на ваших результатах:

$posts = array_map( 'custom_separate_postmeta', $posts );

Просто несколько идей по этой проблеме..

Ответ или решение

Запрос к базе данных WordPress для выборки постов с определенной категорией и их метаданными является типичной задачей для разработчиков, которые стремятся извлечь данные напрямую с помощью MySQL. Рассмотрим, как выполнить такой запрос правильно и эффективно.

Проблема

Изначальный запрос, который вы используете, возвращает пустой результат, когда вы пытаетесь подключить таблицу метаданных постов (wptests_postmeta). Без этой таблицы данные возвращаются корректно, но, конечно, вы теряете важные метаданные.

Анализ проблемы

  1. Множественные метаданные: Таблица postmeta часто содержит множество записей для каждого поста, что создает дубликаты записей в результате JOIN операции. Это объясняет увеличение количества результатов, даже если данные по сути одинаковы.

  2. GROUP BY ограничение: Использование GROUP BY wp.ID вызывает проблему, так как MySQL требует, чтобы все избранные столбцы, которые не агрегированы, были включены в GROUP BY при наличии режима ONLY_FULL_GROUP_BY.

Решение

Решим задачу оптимально используя метод агрегации для метаданных:

SELECT
    wp.ID,
    wp.post_title,
    GROUP_CONCAT(wm.meta_key ORDER BY wm.meta_key DESC SEPARATOR '||') as meta_keys,
    GROUP_CONCAT(wm.meta_value ORDER BY wm.meta_key DESC SEPARATOR '||') as meta_values
FROM
    wptests_posts wp
    INNER JOIN wptests_postmeta wm ON (wm.post_id = wp.ID) AND wm.meta_key NOT LIKE '\_%'
    INNER JOIN wptests_term_relationships wtr ON (wp.ID = wtr.object_id)
    INNER JOIN wptests_term_taxonomy wtt ON (wtr.term_taxonomy_id = wtt.term_taxonomy_id) AND wtt.taxonomy = 'category'
    INNER JOIN wptests_terms wt ON (wt.term_id = wtt.term_id) AND wt.slug = 'uncategorised'
GROUP BY wp.ID

Обработка результатов

После выполнения запроса данные метаданных агрегируются в строках, разделенных ||. Чтобы их использовать, создадим вспомогательную функцию на PHP:

function custom_separate_postmeta($a) {
    $output = array_merge($a, array_combine(explode('||', $a['meta_keys']), array_map('maybe_unserialize', explode('||', $a['meta_values']))));
    unset($output['meta_keys']);
    unset($output['meta_values']);
    return $output;
}

$posts = array_map('custom_separate_postmeta', $posts);

Вывод

Такой подход позволяет сократить количество повторяющихся записей и обрабатывать метаданные на уровне приложения. Это решение хотя и сложное в реализации, но предлагает оптимальный баланс между сложностью и результативностью. Важно также не забывать настройку виртуальной машины либо сервера для обработки больших объемов данных, так как использование функций агрегации может быть ресурсоемким.

Оцените материал
Добавить комментарий

Капча загружается...