我需要获得一堆带有其元数据的帖子。当然,您无法使用标准的帖子查询来获取元数据,因此通常必须为每个帖子执行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:值得吗?在使用2查询方法之前,我要添加多少postmeta表联接?我可以在一个查询中获取所有发布数据,然后在另一个查询中获取所有相关的postmeta,然后在PHP的一个结果集中将元数据与发布数据合并。如果有可能的话,它会比单个复杂的SQL查询更快吗?

我一直认为,“给数据库尽可能多的工作。”不确定在此一个!

评论

我不确定您是否要加入联接。 get_posts()和get_post_meta()的组合可以为您提供相同的数据。实际上,使用联接的效率较低,因为您可能正在检索以后将不使用的数据。

反正不是自动缓存发布的元数据吗?

@rxn,如果我要返回几百个帖子(它们是自定义帖子类型),那么肯定对get_posts()来说是相当大的数据库负载,那么对于其中的每一个,get_post_meta()都会吗? @MannyFleurmond,很难找到有关WP内置缓存的硬信息,但AFAIK会根据请求缓存内容。调用服务器以获取此数据是AJAX调用,我认为没有其他东西可以在它之前获取内容。

实际上,我要进行多个查询并缓存结果。事实证明,我们不仅需要帖子元,包括具有多个值的字段,我们还需要有关通过元字段(其中两组)连接到帖子的用户的数据,以及其中的用户元数据。纯SQL绝对是不可能的!

#1 楼

除非您通过使用WP_Query参数明确告知不要这样做,否则后元信息会自动存储在内存中以用于标准update_post_meta_cache(和主查询)。

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

元缓存如何用于常规查询:

如果update_post_meta_cache参数如果WP_Query的值未设置为false,则从数据库中检索帖子后,将调用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()很聪明,顺便说一句。它不会为已经缓存其元信息的帖子检索元信息。基本上,它不会两次获得相同的元数据。超级高效。

其他附加功能:“为数据库提供尽可能多的工作。” ...不,这是Web。适用不同的规则。通常,如果可行,您总是希望对数据库进行尽可能少的工作。数据库运行缓慢或配置不正确(如果您没有专门配置数据库,则可以押注这是事实)。通常,它们在许多站点之间共享,并且在某种程度上超载。通常,您的Web服务器比数据库更多。通常,您只想尽可能快,简单地从数据库中获取所需的数据,然后使用Web服务器端代码对数据进行排序。当然,作为一般原则,不同情况都是不同的。

#2 楼

我建议使用透视查询。以您的示例为例:
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


评论


该答案应标记为正确。

–卢克
2012年3月13日在6:59

如果您正在寻找数据库查询,这是正确的答案

– Alex Popov
15年4月8日在22:34

当我使用WP_Query时,此查询将我的时间从〜25秒减少到〜3秒。我的要求是只触发一次,因此不需要缓存。

–库什
18年6月3日在15:58



#3 楼

我遇到了一种情况,我也想快速检索大量带有相关元信息的帖子。我需要检索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('||',$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);


平均花费0.7秒。这大约是WP get_post_custom()解决方案的四分之一,而大约是透视查询解决方案的一半。

也许某人对此感兴趣。

评论


我会对使用持久对象缓存解决方案得到的结果感兴趣。对于基本情况,对象缓存有时会变慢,具体取决于您的数据库和配置,但是在大多数主机的情况下,实际结果将产生相差很大的结果。基于内存的缓存非常快。

–奥托
2012年10月13日在3:07

嘿@Otto。无论我使用哪种方法获取数据,我都绝对要缓存结果。我尝试使用瞬态API来执行此操作,但是遇到内存问题。我的2000个对象的序列化字符串的时钟为〜8M,set_transient()失败(内存耗尽)。另外,必须更改max_allowed_pa​​cket MySQL设置。我将研究将其缓存到文件中,但是我不确定那里的性能。有没有一种方法可以缓存到在请求之间持久存在的内存?

– Trev Mills
2012年10月29日20:52

是的,如果您有持久性内存缓存(XCache,memcached,APC等),并使用对象缓存插件(W3 Total Cache支持多种类型的内存缓存),则它将所有对象缓存存储在内存中,从而为您提供了几乎所有东西的倍数加速。

–奥托
2012年10月10日17:09

我返回了6000个要在主干/下划线js过滤方案中使用的项目。这花了6s的自定义查询,由于超时,我什至无法作为WP_Query运行,并将其设为2s查询。尽管array_map减慢了它的速度...

–杰克
2013年12月14日19:51



是否支持构建高性能支持以返回WP_Query中的所有元数据?

– atwellpub
2014年9月12日21:45在

#4 楼

我发现自己处在需要完成此任务以最终从中创建CSV文档的情况下,我最终直接与mysql一起完成了此任务。我的代码将发布表和元表连接起来以检索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 +行,因此它非常大,因此非常慢。

#5 楼

没有SQL版本:

没有SQL即可获取所有帖子及其所有元值(元):

假设您有一个帖子ID列表,存储为ID之类的东西


$post_ids_list = [584, 21, 1, 4, ...];


现在,如果不使用至少一些SQL,就不可能在1个查询中获取所有帖子和所有元数据。 >因此我们必须执行2个查询(仍然只有2个):

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();如果您之后要进行“循环”;))

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(), @Otto指出:首先查看缓存:)

注意:如果您实际上不需要帖子中的其他数据(例如标题,内容等),则只需做2步即可。: -)

#6 楼

使用解决方案形式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);


#7 楼

我也遇到了多值元字段问题。问题出在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_type变量设置为CHAR。我不知道有关将值CAST转换为CHAR会如何影响序列化的字符串的详细信息,但要解决此问题,可以删除强制转换,使SQL看起来像这样:
br />现在,即使可行,您也会对WordPress的内部感到不满,因此其他事情可能会中断,并且如果您需要升级WordPress,这不是永久性的解决方法。

我已修复的方式是复制WordPress生成的SQL以获取所需的元查询,然后编写一些PHP以附加我要查找的meta_values的AND语句,并使用$ wpdb-> get_results($ sql)作为最终输出。哈克,但行得通。

评论


我没有尝试过,但是利用遵循这一行的get_meta_sql过滤器当然比黑客破解核心代码更好。

–史蒂夫·泰勒(Steve Taylor)
2012年4月25日上午10:29