在GET_POST()中为META_QUERY参数设置别名

时间:2017-07-27 作者:Scruffy Paws

运行get_posts()? 我的一个查询执行得很差。为了优化,我只需要能够重用相同的联接表,而不是在只需要一个表时联接三个表。

我当前的示例。。。

$args = array(
    \'meta_query\' => array(
        \'relation\' => \'AND\',
        array(
            \'key\' => \'abc_type\',
            \'value\' => array(\'puppy\', \'kitten\'),
            \'compare\' => \'IN\',
        ),
        array(
            \'relation\' => \'OR\',
            array(
                \'relation\' => \'AND\',
                array(
                    \'key\' => \'abc_type\',
                    \'value\' => \'puppy\',
                    \'compare\' => \'=\',
                ),
                array(
                    \'key\' => \'abc_color\',
                    \'value\' => \'pink\',
                    \'compare\' => \'=\',
                ),
            ),
            array(
                \'relation\' => \'AND\',
                array(
                    \'key\' => \'abc_type\',
                    \'value\' => \'kitten\',
                    \'compare\' => \'=\',
                ),
                array(
                    \'key\' => \'abc_size\',
                    \'value\' => \'large\',
                    \'compare\' => \'=\',
                ),
            ),
        ),
    )
);
get_posts($args);
这在直接SQL中基本上可以转化为这一点。。。

SELECT posts.* FROM posts
INNER JOIN postmeta ON ( posts.ID = postmeta.post_id )
INNER JOIN postmeta AS mt1 ON ( posts.ID = mt1.post_id )
INNER JOIN postmeta AS mt2 ON ( posts.ID = mt2.post_id )
INNER JOIN postmeta AS mt3 ON ( posts.ID = mt3.post_id )
WHERE 1=1
AND
( 
  ( postmeta.meta_key = \'abc_type\' AND postmeta.meta_value IN (\'puppy\',\'kitten\') ) 
  AND 
  ( 
    ( 
      ( mt1.meta_key = \'abc_type\' AND mt1.meta_value = \'puppy\' ) 
      AND 
      ( mt2.meta_key = \'abc_color\' AND mt2.meta_value > \'pink\' )
    ) 
    OR 
    ( 
      ( mt3.meta_key = \'abc_type\' AND mt3.meta_value = \'kitten\' )
      AND
      ( mt4.meta_key = \'abc_size\' AND mt4.meta_value = \'large\' )
    )
  )
) AND posts.post_type = \'abc_mypost\' AND ((posts.post_status = \'publish\'))
GROUP BY posts.ID ORDER BY posts.post_title ASC;
然而,这为自定义元字段添加了2个额外的连接abc_type 因此,这种表现受到了很大的冲击。是否有一种方法可以为多个meta\\u查询参数引用同一别名?大体上mt1mt3 完全没有必要,我应该可以引用第一个postmeta 与第一个一起使用的表( postmeta.meta_key = \'abc_type\' AND postmeta.meta_value IN (\'puppy\',\'kitten\') ). 或者,至少如果我可以为其中的每一个设置自定义别名,我可以引用它。

更优化的查询是。。。

SELECT posts.* FROM posts
INNER JOIN postmeta ON ( posts.ID = postmeta.post_id )
INNER JOIN postmeta AS mt1 ON ( posts.ID = mt1.post_id )
INNER JOIN postmeta AS mt2 ON ( posts.ID = mt2.post_id )
WHERE 1=1
AND
( 
  ( postmeta.meta_key = \'abc_type\' AND postmeta.meta_value IN (\'puppy\',\'kitten\') ) 
  AND 
  ( 
    ( 
      ( postmeta.meta_key = \'abc_type\' AND postmeta.meta_value = \'puppy\' ) 
      AND 
      ( mt1.meta_key = \'abc_color\' AND mt1.meta_value > \'pink\' )
    ) 
    OR 
    ( 
      ( postmeta.meta_key = \'abc_type\' AND postmeta.meta_value = \'kitten\' )
      AND
      ( mt2.meta_key = \'abc_color\' AND mt2.meta_value = \'green\' )
    )
  )
) AND posts.post_type = \'abc_mypost\' AND ((posts.post_status = \'publish\'))
GROUP BY posts.ID ORDER BY posts.post_title ASC;
有什么想法?

4 个回复
SO网友:phatskat

看看meta_query_find_compatible_table_alias 中定义的筛选器wp-includes/class-wp-meta-query.php. 此筛选器的文档:

/**
 * Filters the table alias identified as compatible with the current clause.
 *
 * @since 4.1.0
 *
 * @param string|bool $alias        Table alias, or false if none was found.
 * @param array       $clause       First-order query clause.
 * @param array       $parent_query Parent of $clause.
 * @param object      $this         WP_Meta_Query object.
 */
return apply_filters( \'meta_query_find_compatible_table_alias\', $alias, $clause, $parent_query, $this );
很可能调用函数,find_compatible_table_alias, 返回false,因此查询创建mt* 别名。下面是一些使用此过滤器的示例代码,尽管我个人主张使用一些更容易理解的代码。像这样修改查询可能会带来很多麻烦,而且在查询出错的地方可能根本看不出来,特别是如果您将来引入其他开发人员的话。也就是说。。。

// Reuse the same alias for the abc_type meta key.
function pets_modify_meta_query( $alias, $meta_query ) {
    if ( \'abc_type\' === $meta_query[\'key\'] ) {
        return \'mt1\';
    }

    return $alias;
}

// Filter the query.
add_filter( \'meta_query_find_compatible_table_alias\', \'pets_modify_meta_query\', 10, 2 );

$args = array(
    \'meta_query\' => array(
        \'relation\' => \'AND\',
        array(
            \'key\' => \'abc_type\',
            \'value\' => array(\'puppy\', \'kitten\'),
            \'compare\' => \'IN\',
        ),
        array(
            \'relation\' => \'OR\',
            array(
                \'relation\' => \'AND\',
                array(
                    \'key\' => \'abc_type\',
                    \'value\' => \'puppy\',
                    \'compare\' => \'=\',
                ),
                array(
                    \'key\' => \'abc_color\',
                    \'value\' => \'pink\',
                    \'compare\' => \'=\',
                ),
            ),
            array(
                \'relation\' => \'AND\',
                array(
                    \'key\' => \'abc_type\',
                    \'value\' => \'kitten\',
                    \'compare\' => \'=\',
                ),
                array(
                    \'key\' => \'abc_size\',
                    \'value\' => \'large\',
                    \'compare\' => \'=\',
                ),
            ),
        ),
    )
);

$q = new WP_Query($args);
echo \'<pre>\', print_r($q->request, true); die;
这将导致如下查询

SELECT SQL_CALC_FOUND_ROWS
    wp_posts.ID
FROM wp_posts
INNER JOIN wp_postmeta ON ( wp_posts.ID = wp_postmeta.post_id )
INNER JOIN wp_postmeta AS mt1 ON ( wp_posts.ID = mt1.post_id )
WHERE
    1=1
AND
(
    ( mt1.meta_key = \'abc_type\' AND mt1.meta_value IN (\'puppy\',\'kitten\') )
    AND
    (
        (
            ( mt1.meta_key = \'abc_type\' AND mt1.meta_value = \'puppy\' )
            AND
            ( wp_postmeta.meta_key = \'abc_color\' AND wp_postmeta.meta_value = \'pink\' )
        )
        OR
        (
            ( mt1.meta_key = \'abc_type\' AND mt1.meta_value = \'kitten\' )
            AND
            ( mt1.meta_key = \'abc_size\' AND mt1.meta_value = \'large\' )
        )
    )
)
AND
    wp_posts.post_type = \'post\'
AND (
    wp_posts.post_status = \'publish\'
    OR
    wp_posts.post_status = \'future\'
    OR
    wp_posts.post_status = \'draft\'
    OR wp_posts.post_status = \'pending\'
)
GROUP BY wp_posts.ID ORDER BY wp_posts.post_date DESC LIMIT 0, 10

SO网友:Nathan Johnson

您可以使用posts_whereposts_join 用于修改查询的筛选器。这不是很优雅,但你应该能够搞乱这两个过滤器,这样你的sql就更优化了。这是一种蛮力,但我在WP\\u查询类中找不到更好的方法。但这并不是说没有。

//* Make sure to not suppress filters
$args = array(
  \'suppress_filters\' => false,
  //* rest of args unchanged
);

add_filter( \'posts_where\', function( $sql ) {
  $sql = str_replace(
    "( mt1.meta_key = \'abc_type\' AND mt1.meta_value = \'puppy\' )",
    "( postmeta.meta_key = \'abc_type\' AND postmeta.meta_value = \'puppy\' )",
    $sql
  );

  $sql = str_replace(
    "( mt3.meta_key = \'abc_type\' AND mt3.meta_value = \'kitten\' )",
    "( postmeta.meta_key = \'abc_type\' AND postmeta.meta_value = \'kitten\' )",
    $sql
  );

  $sql = str_replace( [ \'mt2\', \'mt4\' ], [ \'mt1\', \'mt2\' ], $sql );
  return $sql;
});

add_filter( \'posts_join\', function( $sql ) {
  $sql = str_replace(
    " INNER JOIN wp_postmeta AS mt4 ON ( wp_posts.ID = mt4.post_id )",
    "",
    $sql
  );
  $sql = str_replace(
    " INNER JOIN wp_postmeta AS mt3 ON ( wp_posts.ID = mt3.post_id )",
    "",
    $sql
  );
  return $sql;
});
其中可能应该有一些检查,以便您不会意外地修改其他查询。这是留给读者的练习。

SO网友:Vlad Olaru

您可以通过删除第一个元查询来优化查询,因为它是冗余的,如下所示:

$args = array(
    \'meta_query\' => array(
        \'relation\' => \'OR\',
        array(
            \'relation\' => \'AND\',
            array(
                \'key\' => \'abc_type\',
                \'value\' => \'puppy\',
                \'compare\' => \'=\',
            ),
            array(
                \'key\' => \'abc_color\',
                \'value\' => \'pink\',
                \'compare\' => \'=\',
            ),
        ),
        array(
            \'relation\' => \'AND\',
            array(
                \'key\' => \'abc_type\',
                \'value\' => \'kitten\',
                \'compare\' => \'=\',
            ),
            array(
                \'key\' => \'abc_size\',
                \'value\' => \'large\',
                \'compare\' => \'=\',
            ),
        ),
    ),
);
get_posts($args);
这样你只能得到pink puppylarge kitten, 正如你所想,我相信。

至于优化WordPress的内部MySQL查询,我认为您应该远离这一点,因为您可能会暴露出可能的副作用。您最好依赖缓存查询的事实,并在(更大的)数据集上进行更多的PHP处理。我相信这将带来更好的总体性能,因为瓶颈不在于从数据库中提取的数据量,而在于收集数据的难度(查询的数量)。PHP通过数组的速度非常快。

因此,考虑到post meta被缓存,我认为这样的情况更快:

$args = array(
    \'meta_query\' => array( 
        array(
            \'key\' => \'abc_type\',
            \'value\' => array(\'puppy\', \'kitten\'),
            \'compare\' => \'IN\',
        ),
    ),
);

$final_posts = array();
foreach( $get_posts($args) as $post ) {
    if ( \'puppy\' === get_post_meta( $post->ID, \'abc_type\', true ) ) {
        if ( \'pink\' === get_post_meta( $post->ID, \'abc_color\', true ) ) {
            $final_posts[] = $post;
        }
    } else {
        // This is definitely a kitten
        if ( \'large\' === get_post_meta( $post->ID, \'abc_size\', true ) ) {
            $final_posts[] = $post;
        }
    }
}

SO网友:Rick Hellewell

我不是一个真正的数据库专家,但我曾经在电视上玩过一次。。。

这部分不是吗

SELECT posts.* FROM posts
INNER JOIN postmeta ON ( posts.ID = postmeta.post_id )
INNER JOIN postmeta AS mt1 ON ( posts.ID = mt1.post_id )
INNER JOIN postmeta AS mt2 ON ( posts.ID = mt2.post_id )
最好替换为

SELECT posts.* FROM posts
INNER JOIN postmeta ON (( posts.ID = postmeta.post_id ) and 
( posts.ID = mt1.post_id ) and
( posts.ID = mt2.post_id ))
这可能会被进一步简化。。在适当的位置插入一些别名,以便您可以使用查询的其余部分。

只是一个想法。。。

结束

相关推荐

使用新的WP-Query()从循环中过滤后期格式;

嗨,我目前正在为我的博客构建一个主题。下面的代码指向最新的帖子(特色帖子)。因为这将有一个不同的风格比所有其他职位。然而我想过滤掉帖子格式:链接使用我在循环中定义的WP查询,因为它给我带来了更多的灵活性。我该怎么做呢? <?php $featured = new WP_Query(); $featured->query(\'showposts=1\'); ?> <?php while ($featured->have_post