问题描述

我需要得到一堆他们的元数据的帖子。当然,您无法使用标准的帖子查询获取元数据,因此您通常必须为每个帖子执行 get_post_custom()

我正在尝试使用一个自定义查询,如下所示:

$results = $wpdb->get_results("
    SELECT  p.ID,
        p.post_title,
        pm1.meta_value AS first_field,
        pm2.meta_value AS second_field,
        pm3.meta_value AS third_field
    FROM    $wpdb->posts p LEFT JOIN $wpdb->postmeta pm1 ON (
            pm1.post_id = p.ID  AND
            pm1.meta_key    = 'first_field_key'
        ) LEFT JOIN $wpdb->postmeta pm2 ON (
            pm2.post_id = p.ID  AND
            pm2.meta_key    = 'second_field_key'
        ) LEFT JOIN $wpdb->postmeta pm3 ON (
            pm3.post_id = p.ID  AND
            pm3.meta_key    = 'third_field_key'
        )
    WHERE   post_status = 'publish'
");

似乎工作如果您使用任何这些元字段,可以在同一个文章中允许其多个元值的方式,那么它会跳过。我不能想到一个加入来做到这一点。

那么,问题 1:有没有加入,sub-query 或什么,带入 multiple-value 元字段?

但问题 2:值得吗?在添加 2 查询方法之前,需要添加多少个 postmeta 表连接?我可以在一个查询中抓取所有的帖子数据,然后在另一个查询中获取所有相关的后期数据,并将元数据与 PHP 中的一个结果集中的后期数据相结合。最终会比单个 ever-more-complex SQL 查询更快吗?

我一直认为,「尽可能多地向数据库工作。」 不确定这个!

最佳解决方案

发布元信息自动缓存在内存中用于标准 WP_Query(和主查询),除非您明确告知不使用 update_post_meta_cache 参数。

因此,您不应该为此编写自己的查询。

元缓存如何适用于普通查询:

如果 WP_Query 的 update_post_meta_cache 参数未设置为 false,则在从 DB 中检索到帖子后,将调用 update_post_caches() 函数,然后调用 update_postmeta_cache() 。

update_postmeta_cache()功能是 update_meta_cache()的封装,它本质上调用了一个简单的 SELECT,其中检索到所有帖子的 ID 。这将获得查询中所有帖子的所有 postmeta,并将该数据保存在对象缓存中 (使用 wp_cache_add()) 。

当你做一些像 get_post_custom()这样的事情,它首先检查对象缓存。所以在这一点上没有额外的查询来获取帖子元数据。如果你已经在 WP_Query 中找到了这个帖子,那么这个元数据已经在内存里了,直接从那里得到。

这里的优点比复杂的查询大很多倍,但最大的优点在于使用对象缓存。如果您使用像 XCache 或 memcached 或 APC 这样的持久性内存缓存解决方案,并且具有可以将对象缓存绑定到其上的插件 (例如,W3 Total Cache),则整个对象缓存将被存储在快速内存中已经。在这种情况下,检索您的数据需要零查询; 它已经在记忆中了持久性对象缓存在许多方面都非常棒。

换句话说,您的查询可能会比使用正确的查询和简单的持久内存解决方案加载和加载更慢。使用正常的 WP_Query 。节省一些努力。

附加:update_meta_cache()是智能的,BTW 。它不会为已经有其元信息缓存的帖子检索元信息。基本上没有得到相同的两次元。超高效。

额外的附加:「尽可能多的工作到数据库。」… 不,这是网络。不同规则适用。一般来说,如果可行,您总是希望尽可能少地向数据库提供任何工作。数据库缓慢或配置不佳 (如果您没有具体配置,您可以赌钱,这是真的) 。通常它们在许多站点之间共享,并在一定程度上超载。通常你有比数据库更多的 Web 服务器。一般来说,您只需要尽可能快速地将数据从数据库中获取,然后使用 web-server-side 代码进行排序。作为一般原则,当然不同的情况都是不同的。

次佳解决方案

我会推荐一个枢轴查询。使用你的例子:

SELECT  p.ID,
        p.post_title,
        MAX(CASE WHEN wp_postmeta.meta_key = 'first_field' then wp_postmeta.meta_value ELSE NULL END) as first_field,
        MAX(CASE WHEN wp_postmeta.meta_key = 'second_field' then wp_postmeta.meta_value ELSE NULL END) as second_field,
        MAX(CASE WHEN wp_postmeta.meta_key = 'third_field' then wp_postmeta.meta_value ELSE NULL END) as third_field,

 FROM    wp_posts p LEFT JOIN wp_postmeta pm1 ON ( pm1.post_id = p.ID)
GROUP BY
   wp_posts.ID,wp_posts.post_title

第三种解决方案

我遇到一个案例,我想也想快速检索大量的帖子与他们相关联的元信息。我需要检索 O(2000) 的帖子。

我尝试使用 Otto 的建议 – 为所有帖子运行 WP_Query :: query,然后为每个帖子循环并运行 get_post_custom 。平均来说,这需要约 3 秒才能完成。

然后我尝试了 Ethan 的透视查询 (虽然我不喜欢手动请求我感兴趣的每个 meta_key) 。我仍然不得不循环通过所有检索的帖子来取消串行化 meta_value 。平均来说,这需要大约 1.3 秒才能完成。

然后我尝试使用 GROUP_CONCAT 功能,并找到最好的结果。以下是代码:

global $wpdb;
$wpdb->query('SET SESSION group_concat_max_len = 10000'); // necessary to get more than 1024 characters in the GROUP_CONCAT columns below
$query = "
    SELECT p.*,
    GROUP_CONCAT(pm.meta_key ORDER BY pm.meta_key DESC SEPARATOR '||') as meta_keys,
    GROUP_CONCAT(pm.meta_value ORDER BY pm.meta_key DESC SEPARATOR '||') as meta_values
    FROM $wpdb->posts p
    LEFT JOIN $wpdb->postmeta pm on pm.post_id = p.ID
    WHERE p.post_type = 'product' and p.post_status = 'publish'
    GROUP BY p.ID
";

$products = $wpdb->get_results($query);

// massages the products to have a member ->meta with the unserialized values as expected
function massage($a){
    $a->meta = array_combine(explode('||',$product->meta_keys),array_map('maybe_unserialize',explode('||',$product->meta_values)));
    unset($a->meta_keys);
    unset($a->meta_values);
}

$products = array_map('massage',$products);

平均 0.7 秒。这是 WP get_post_custom() 解决方案大约四分之一的时间和大约一半的数据查询解决方案。

也许这对某人有兴趣。

参考文献

注:本文内容整合自 Google/Baidu/Bing 辅助翻译的英文资料结果。如果您对结果不满意,可以加入我们改善翻译效果:薇晓朵技术论坛。