Улучшение запроса с использованием множества внутренних соединений с wp postmeta, таблицей ключ / значение

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

Я хотел бы знать, что может быть лучшим способом сделать это

SELECT *
FROM wp_posts
INNER JOIN wp_postmeta color ON wp_posts.ID = color.post_id 
INNER JOIN wp_postmeta transmission ON wp_posts.ID = transmission.post_id 
INNER JOIN wp_postmeta model ON wp_posts.ID = model.post_id 
INNER JOIN wp_postmeta brand ON wp_posts.ID = brand.post_id 

AND color.meta_key = 'color' 
AND color.meta_value = 'red' 
AND transmission.meta_key = 'transmission' 
AND transmission.meta_value = 'auto' 
AND model.meta_key = 'model' 
AND model.meta_value = 'model' 
AND brand.meta_key = 'brand' 
AND brand.meta_value = 'brand'

AND wp_posts.post_status = 'publish'
AND wp_posts.post_type = 'car'
ORDER BY wp_posts.post_title

вот результат объяснения.

+----+-------------+-----------+--------+-----------------------------+----------+---------+------------------------+------+----------------------------------------------+
| id | select_type | table         | type   | possible_keys               | key      | key_len | ref                          | rows | Extra                                        |
+----+-------------+-----------+--------+-----------------------------+----------+---------+------------------------+------+----------------------------------------------+
|  1 | SIMPLE      | color         | ref    | post_id,meta_key            | meta_key | 768     | const                        |  629 | Using where; Using temporary; Using filesort |
|  1 | SIMPLE      | wp_posts      | eq_ref | PRIMARY,type_status_date,ID | PRIMARY  | 8       | tmcdb.color.post_id          |    1 | Using where                                  |
|  1 | SIMPLE      | brand         | ref    | post_id,meta_key            | post_id  | 8       | tmcdb.wp_posts.ID            |    4 | Using where                                  |
|  1 | SIMPLE      | transmission  | ref    | post_id,meta_key            | post_id  | 8       | tmcdb.color.post_id          |    4 | Using where                                  |
|  1 | SIMPLE      | model         | ref    | post_id,meta_key            | post_id  | 8       | tmcdb.transmission.post_id   |    4 | Using where                                  |
+----+-------------+-----------+--------+-----------------------------+----------+---------+------------------------+------+----------------------------------------------+

схема Wordpress здесь.

8 ответов


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

совет: никогда использовать SELECT * в программном обеспечении, если вы не знаете, почему вы это делаете. Особенно с запросами, содержащими много JOIN операции SELECT * возвращает множество бессмысленных и избыточных столбцов.

есть запрос дизайн трюк, чтобы знать для WordPress postmeta таблица. Если вы хотите получить определенный атрибут, сделайте следующее:

 SELECT p.ID, p.post_title,
        color.meta_value AS color
   FROM wp_posts AS p
   LEFT JOIN wp_postmeta AS color ON p.ID = color.post_id AND 'color' = color.meta_key
  WHERE p.post_status = 'publish'
    AND /* etc etc */

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


это база данных Wordpress, и вы можете неохотно вносить обширные изменения в схему, потому что это может сломать другие части приложения или усложнить обновление в будущем.

сложность этого запроса показывает один из недостатков объект-атрибут-значение дизайн. Этот дизайн является гибким, поскольку он позволяет создавать новые атрибуты во время выполнения, но он делает много запросов к таким данным более сложными, чем они были бы с обычным столом.

схема для Wordpress не была оптимизирована хорошо. есть некоторые наивные ошибки индексации, даже в самой последней версии 4.0.

для этого конкретного запроса помогают следующие два индекса:

CREATE INDEX `bk1` ON wp_postmeta (`post_id`,`meta_key`,`meta_value`(255));

CREATE INDEX `bk2` ON wp_posts (`post_type`,`post_status`,`post_title`(255));

на bk1 index помогает найти именно правильный мета-ключ и значение.

на bk2 index помогает избежать filesort.

эти индексы не могут быть охватывающие индексы, потому что post_title и meta_value are TEXT столбцы, и они слишком длинные, чтобы быть полностью индексированными. Вам придется изменить их на VARCHAR(255). Но это рискует нарушить приложение, если оно зависит от хранения более длинных строк в этой таблице.

+----+-------------+--------------+------------+------+---------------+------+---------+----------------------------+------+----------+-----------------------+
| id | select_type | table        | partitions | type | possible_keys | key  | key_len | ref                        | rows | filtered | Extra                 |
+----+-------------+--------------+------------+------+---------------+------+---------+----------------------------+------+----------+-----------------------+
|  1 | SIMPLE      | wp_posts     | NULL       | ref  | bk2           | bk2  | 124     | const,const                |    1 |   100.00 | Using index condition |
|  1 | SIMPLE      | color        | NULL       | ref  | bk1           | bk1  | 1542    | wp.wp_posts.ID,const,const |    1 |   100.00 | Using index           |
|  1 | SIMPLE      | transmission | NULL       | ref  | bk1           | bk1  | 1542    | wp.wp_posts.ID,const,const |    1 |   100.00 | Using index           |
|  1 | SIMPLE      | model        | NULL       | ref  | bk1           | bk1  | 1542    | wp.wp_posts.ID,const,const |    1 |   100.00 | Using index           |
|  1 | SIMPLE      | brand        | NULL       | ref  | bk1           | bk1  | 1542    | wp.wp_posts.ID,const,const |    1 |   100.00 | Using index           |
+----+-------------+--------------+------------+------+---------------+------+---------+----------------------------+------+----------+-----------------------+

чтобы решить проблему производительности с 10 + присоединяет SQL-запросы к таблицам innodb с помощью кодировки utf8, создайте новый индекс на postmeta:

сначала резервное копирование базы данных. Уменьшить [wp_]postmeta.meta_key длина до 191, чтобы избежать "указанный ключ был слишком длинным; максимальная длина ключа составляет 767 байт" ошибка.

ALTER TABLE wp_postmeta MODIFY meta_key VARCHAR(191);

создать индекс

CREATE INDEX wpm_ix ON wp_postmeta (post_id, meta_key);


для производительности попробуйте:

будьте явными для столбцов, которые вы хотите вытащить. Посмотрите, какие индексы вам могут понадобиться или не понадобиться. Ограничить количество вытягиваемых строк.


это лучше?

SELECT
            P.*,
            C.`meta_value` color,
            T.`meta_value` transmission,
            M.`meta_value` model,
            B.`meta_value` brand
    FROM
            `wp_posts` P
        JOIN
            `wp_postmeta` C
                ON P.`ID` = C.`post_id` AND C.`meta_key` = 'color'
        JOIN
            `wp_postmeta` T
                ON P.`ID` = T.`post_id` AND T.`meta_key` = 'transmission'
        JOIN
            `wp_postmeta` M
                ON P.`ID` = M.`post_id` AND M.`meta_key` = 'model'
        JOIN
            `wp_postmeta` B
                ON P.`ID` = B.`post_id` AND B.`meta_key` = 'brand'
    WHERE
            C.`meta_value` = 'red'
        AND
            T.`meta_value` = 'auto'
        AND
            M.`meta_value` = 'model'
        AND
            B.`meta_value` = 'brand'
        AND
            P.`post_status` = 'publish'
        AND
            P.`post_type` = 'car'
    ORDER BY
            P.`post_title`

если его все еще медленно, что, вероятно, будет, попробуйте добавить этот индекс,

CREATE INDEX `IX-wp_postmeta-post_id-meta_key-meta_value`
    ON `wp_postmeta` (`post_id`, `meta_key`, `meta_value`);

вы также можете попробовать добавить этот показатель к wp_post

CREATE UNIQUE INDEX `IX-wp_post-post_status-post_type-post_title-ID`
    ON `wp_post` (`post_stauts`, `post_type`, `post_title`, `ID`);

чем больше вы можете ограничить список выбора, (бит между SELECT и FROM,) тем лучше. Нет смысла возвращать много данных, которые вы не будете использовать. Вы получите лучшую производительность, если весь список выбора "покрыт" индексом.


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

SELECT [wp_posts fields you care about], wp_postmeta.meta_key, wp_postmeta.meta_value
FROM wp_posts
INNER JOIN wp_postmeta ON wp_posts.ID = wp_postmeta.post_id 
WHERE wp_posts.post_status = 'publish' AND wp_posts.post_type = 'car'
   AND wp_postmeta.meta_key IN ('color', 'transmission', 'model', 'brand')
ORDER BY wp_posts.post_title, wp_postmeta.meta_key, wp_postmeta.meta_value;

или, вы могли бы сделать что-то подобное...

SELECT [wp_posts fields desired], COUNT(*) AS matchCount
FROM wp_posts INNER JOIN wp_postmeta ON wp_posts.ID = wp_postmeta.post_id
WHERE wp_posts.post_status = 'publish' AND wp_posts.post_type = 'car'
    AND ((meta_key = 'color' AND meta_value = 'red')
        OR (meta_key = 'transmission' AND meta_value = 'auto')
        OR [etc...]
        )
GROUP BY wp_posts.ID
HAVING matchCount = [number of key-value pairs you're checking]
;

в WordPress есть хороший инструмент запроса WP_Query. Для поиска в post meta values вы можете использовать этот код:

$args = array(
    'post_type'  => 'post',
    'meta_query' => array(
            array(
                'key'     => 'fieldname',
                'value'   => 'fieldvalue',
                'compare' => 'LIKE',
            ),
        ),
    );
    $query = new WP_Query( $args );
    if ( $query->have_posts() ) {
        while ( $query->have_posts() ) {
            $query->the_post();
            $custom = get_post_custom();

            print_r($post);
            print_r($custom);
        }
    } else {
        // no posts found
    }
    wp_reset_postdata();

для получения дополнительной информации об API запросов посетите этот сайт, есть множество примеров: http://codex.wordpress.org/Class_Reference/WP_Query