获取帖子的最有效方式

时间:2012-01-10 作者:Steve Taylor

我需要得到一堆带有元数据的帖子。当然,您无法使用标准posts查询获取元数据,因此您通常必须执行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:是否有一个连接、子查询或其他方式来引入多个值元字段?

但问题2:值得吗?有多少postmeta 在2查询方法变得更好之前,我应该添加表联接吗?我可以在一个查询中获取所有post数据,然后在另一个查询中获取所有相关的POSETA,并在PHP的一个结果集中将元数据与post数据相结合。如果可能的话,这会比一个更加复杂的SQL查询更快吗?

我总是想,“给数据库尽可能多的工作。”这件不太确定!

7 个回复
SO网友:Otto

Post meta信息自动缓存在标准的内存中WP_Query (和主查询),除非您使用update_post_meta_cache 参数

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

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

如果update_post_meta_cache 参数设置为WP_Query 未设置为false,则从DB检索帖子后update_post_caches() 函数将被调用,而函数反过来调用update_postmeta_cache().

这个update_postmeta_cache() 函数是的包装器update_meta_cache(), 它本质上称为SELECT 所有检索到的帖子的ID。这将使它获得查询中所有帖子的所有postmeta,并将该数据保存在对象缓存中(使用wp_cache_add()).

当你做这样的事情get_post_custom(), 它首先检查对象缓存。因此,在这一点上,它不会进行额外的查询来获取post meta。如果你在WP_Query, 然后,元已经在内存中,它直接从内存中获取它。

这里的优势是进行复杂查询的许多倍,但最大的优势来自使用对象缓存。如果您使用诸如XCache、memcached或APC之类的持久内存缓存解决方案,并且有一个插件可以将对象缓存与之绑定(例如W3 Total cache),那么整个对象缓存已经存储在快速内存中。在这种情况下,检索数据需要零次查询;它已经在记忆中了。持久对象缓存在许多方面都非常棒。

换句话说,您的查询的加载速度可能越来越慢,而不是使用适当的查询和简单的持久内存解决方案。使用普通WP_Query. 省点力气吧。

Additional: update_meta_cache() 是智能的,顺便说一句。它不会检索已经缓存了元数据信息的帖子的元数据信息。基本上,它不会得到两次相同的meta。超级高效。

Additional additional: “给数据库尽可能多的工作。”。。。不,这是网络。适用不同的规则。通常,如果可行的话,您总是希望尽可能少地对数据库进行工作。数据库速度慢或配置不好(如果您没有专门配置它,您可以打赌这是真的)。它们通常在许多站点之间共享,并且在某种程度上超载。通常,您的web服务器比数据库多。一般来说,您只需要尽可能快速、简单地从DB中获取所需的数据,然后使用web服务器端代码对其进行排序。当然,作为一般原则,不同的情况都是不同的。

SO网友:Ethan Seifert

我建议使用透视查询。使用您的示例:

SELECT  p.ID,   
        p.post_title, 
        MAX(CASE WHEN pm1.meta_key = \'first_field\' then pm1.meta_value ELSE NULL END) as first_field,
        MAX(CASE WHEN pm1.meta_key = \'second_field\' then pm1.meta_value ELSE NULL END) as second_field,
        MAX(CASE WHEN pm1.meta_key = \'third_field\' then pm1.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
   p.ID,p.post_title

SO网友:Trevor Mills

我遇到了一个案例,我还想快速检索大量带有相关元信息的帖子。我需要检索O(2000)个帖子。

我使用Otto的建议进行了尝试-为所有帖子运行WP\\u Query::Query,然后循环并为每个帖子运行get\\u post\\u custom。This took, on average, about 3 seconds to complete.

然后我尝试了Ethan的pivot查询(尽管我不喜欢手动询问我感兴趣的每个meta\\u键)。我仍然需要遍历所有检索到的帖子来取消meta\\u值的序列化。This took, on average, about 1.3 seconds to complete.

然后我尝试使用GROUP\\u 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(\'||\',$a->meta_keys),array_map(\'maybe_unserialize\',explode(\'||\',$a->meta_values)));
    unset($a->meta_keys);
    unset($a->meta_values);
    return $a;
}

$products = array_map(\'massage\',$products);
This took on average 0.7 seconds. 这大约是WP get\\u post\\u custom()解决方案的四分之一,大约是pivot查询解决方案的一半。

也许有人会对此感兴趣。

SO网友:Terry Kernan

我发现自己处于一种需要完成此任务才能最终从中创建CSV文档的情况下,我最终直接使用mysql来完成此任务。我的代码连接post和meta表以检索woocommerce定价信息,之前发布的解决方案要求我在sql中使用表别名才能正常工作。

SELECT p.ID, p.post_title, 
    MAX(CASE WHEN pm1.meta_key = \'_price\' then pm1.meta_value ELSE NULL END) as price,
    MAX(CASE WHEN pm1.meta_key = \'_regular_price\' then pm1.meta_value ELSE NULL END) as regular_price,
    MAX(CASE WHEN pm1.meta_key = \'_sale_price\' then pm1.meta_value ELSE NULL END) as sale_price,
    MAX(CASE WHEN pm1.meta_key = \'_sku\' then pm1.meta_value ELSE NULL END) as sku
    FROM wp_posts p LEFT JOIN wp_postmeta pm1 ON ( pm1.post_id = p.ID)                 
    WHERE p.post_type in(\'product\', \'product_variation\') AND p.post_status = \'publish\'
    GROUP BY p.ID, p.post_title
但请注意,woocommerce在我的元表中创建了超过300K行,所以它非常大,因此速度非常慢。

SO网友:jave.web

无SQL版本:

在无SQL的情况下获取所有帖子及其所有元值(meta):

假设您有一个post ID列表,存储为ID数组,类似

$post_ids_list = [584, 21, 1, 4, ...];
现在,如果不使用至少一点SQL,在一个查询中获取所有帖子和所有元是不可能的,因此我们必须执行两个查询(仍然只有两个):

1。获取所有帖子(使用WP_Query )

$request = new WP Query([
  \'post__in\' => $post_ids_list,
  \'ignore_sticky_posts\' => true, //if you want to ignore the "stickiness"
]);
(别忘了打电话wp_reset_postdata(); 如果您正在执行"loop" 之后;))

2。更新元缓存

//don\'t be confused here: "post" means content type (post X user X ...), NOT post type ;)
update_meta_cache(\'post\', $post_ids_list);
要获取元数据,只需使用标准get_post_meta() 正如奥托所指出的:
looks into cache first :)

Note: 如果你实际上不需要帖子中的其他数据(如标题、内容等)you can do just 2. :-)

SO网友:Jonathan Joosten

使用trevor解决方案表单并将其修改为使用嵌套SQL。这没有经过测试。

global $wpdb;
$query = "
    SELECT p.*, (select pm.* From $wpdb->postmeta AS pm WHERE pm.post_id = p.ID)
    FROM $wpdb->posts p 
    WHERE p.post_type = \'product\' and p.post_status = \'publish\' 
";
$products = $wpdb->get_results($query);

SO网友:Harry Love

我也遇到了多值元字段问题。问题在于WordPress本身。查看wp includes/meta。php。查找以下行:

$where[$k] = \' (\' . $where[$k] . $wpdb->prepare( "CAST($alias.meta_value AS {$meta_type}) {$meta_compare} {$meta_compare_string})", $meta_value );
问题在于CAST语句。在元值查询中,$meta\\u type变量设置为CHAR。我不知道将值强制转换为CHAR如何影响序列化字符串的详细信息,但要修复它,可以删除强制转换,使SQL如下所示:

$where[$k] = \' (\' . $where[$k] . $wpdb->prepare( "$alias.meta_value {$meta_compare} {$meta_compare_string})", $meta_value );
现在,即使这样做有效,但您正在破坏WordPress的内部结构,因此其他东西可能会损坏,并且假设您需要升级WordPress,这并不是一个永久性修复。

我修复它的方法是复制WordPress为我想要的元查询生成的SQL,然后编写一些PHP来为我要查找的元值附加额外的and语句,并使用$wpdb->get\\u results($SQL)进行最终输出。Hacky,但它有效。

结束

相关推荐

调试WordPress SQL调用的首选方法?

在编写自定义后台SQL调用等时,您最喜欢的调试Wordpress应用程序的方法是什么?来自Rails背景,我通常跟踪一个日志文件,该文件不仅显示单独加载到模板中的所有进程和视图,而且如果出现错误,它将锁定exact 错误的位置,以及出现错误的建议。我注意到在WordPress中,您可以设置调试。日志文件,但它只显示了此文件的一个非常简单的版本,很少指出实际失败的地方。有什么建议吗?