使用大量内部联接来改进查询到wp_postmeta,一个键/值表

时间:2014-10-11 21:50:40

标签: mysql sql database wordpress entity-attribute-value

我正在使用正在执行以下查询的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 schema here.

8 个答案:

答案 0 :(得分:11)

您似乎正在尝试获取每个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是一种特殊类型的表,称为存储。这里发生了什么?一些事情:

  1. 使用此模式,每个帖子都会有一行,posts表中的某些列和postmeta表中的特定属性。
  2. 您是LEFT JOIN postmeta表格,因此如果遗失该属性,您仍会获得一行。
  3. 您正在使用postmeta表的别名。这是postmeta AS color
  4. 您要在加入的meta_key条件中加入'color' = color.meta_key的选择器(此处为ON)。
  5. 您在SELECT子句中使用别名,以使用适当的列名称显示postmeta.meta_value项目。这是color.meta_value AS color
  6. 一旦习惯了这种模式,就可以通过一系列LEFT JOIN操作将其叠加起来,以获得许多不同的属性,就像这样。

         SELECT wp_posts.ID, wp_posts.post_title, wp_posts.whatever,
                color.meta_value        AS color,
                transmission.meta_value AS transmission,
                model.meta_value        AS model,
                brand.meta_value        AS brand
           FROM wp_posts
    
      LEFT JOIN wp_postmeta  AS color 
             ON wp_posts.ID = color.post_id        AND color.meta_key='color'
    
      LEFT JOIN wp_postmeta  AS transmission
             ON wp_posts.ID = transmission.post_id AND transmission.meta_key='transmission'
    
      LEFT JOIN wp_postmeta  AS model
             ON wp_posts.ID = model.post_id        AND model.meta_key='model'
    
      LEFT JOIN wp_postmeta  AS  brand
             ON wp_posts.ID = brand.post_id        AND brand.meta_key='brand'
    
          WHERE wp_posts.post_status = 'publish'
            AND wp_posts.post_type = 'car'
       ORDER BY wp_posts.post_title
    

    我在这个查询上做了一堆缩进,以便更容易看到模式。您可能更喜欢不同的缩进风格。

    很难知道为什么在您的问题中出现查询性能问题。这可能是因为您正在使用所有INNER JOIN操作进行组合爆炸,然后进行过滤。但无论如何,您展示的查询可能不会返回任何行。

    如果您仍遇到性能问题,请尝试在postmeta列的(post_id, meta_key, meta_value)上创建复合索引。如果你正在创建一个WordPress插件,这可能是插件安装时的一项工作。

答案 1 :(得分:3)

这是一个Wordpress数据库,您可能不愿意对架构进行大量更改,因为它可能会破坏应用程序的其他部分或将来复杂化升级。

此查询的难度显示了设计的缺点之一。该设计非常灵活,因为它允许在运行时创建新属性,但它使得对这些数据的大量查询比使用传统表格更复杂。

The schema for Wordpress has not been optimized well.即使在最新的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索引有助于查找正确的元键值。

bk2索引有助于避免文件排序。

这些索引不能覆盖索引,因为post_titlemeta_valueTEXT列,而且这些索引太长而无法完全索引。您必须将它们更改为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           |
+----+-------------+--------------+------------+------+---------------+------+---------+----------------------------+------+----------+-----------------------+

答案 2 :(得分:3)

要解决使用utf8字符集在innodb表上添加10个以上SQL查询的性能问题,请在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);

答案 3 :(得分:1)

对于性能尝试:

明确要提取的列。 查看您可能需要或不需要的索引。 限制被拉动的行数。

答案 4 :(得分:1)

这样更好吗?

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`);

您可以限制选择列表的次数越多(SELECTFROM之间的位数),就越好。没有必要返回大量您不会使用的数据。如果整个选择列表被索引“覆盖”,您将获得最佳性能。

答案 5 :(得分:1)

假设您实际上可以更改处理结果的代码,我会使它更简单的查询并使用代码来过滤结果。

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]
;

答案 6 :(得分:1)

在WordPress中有一个很好的查询工具WP_Query。要搜索post meta值,您可以使用以下代码:

$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

答案 7 :(得分:1)