Le moyen le plus efficace d'obtenir des publications avec postmeta

32

Je dois obtenir un tas de messages avec leurs métadonnées. Bien sûr, vous ne pouvez pas obtenir de métadonnées avec une requête posts standard, vous devez donc généralement faire un get_post_custom() pour chaque message.

J'essaie avec une requête personnalisée, comme ceci:

$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'
");

semble fonctionner. Il se déclenche si vous utilisez l'un de ces champs méta d'une manière qui lui permet d'utiliser plusieurs méta-valeurs pour le même message. Je ne peux pas penser à une jointure pour le faire.

Donc, question 1: existe-t-il une jointure, une sous-requête ou autre chose pour importer des méta-champs à valeurs multiples?

Mais question 2: cela en vaut-il la peine? Combien de jointures de table postmeta dois-je ajouter avant qu'une approche à 2 requêtes devienne préférable? Je pouvais saisir toutes les données de publication dans une requête, puis toutes les publications pertinentes dans une autre et combiner la méta avec les données de publication dans un jeu de résultats en PHP. Est-ce que cela finirait par être plus rapide qu'une requête SQL toujours plus complexe, si cela est possible?

Je pense toujours: "Donnez autant de travail que possible à la base de données." Pas sûr sur celui-ci!

    
posée Steve Taylor 10.01.2012 - 23:18
la source

7 réponses

55

Les métadonnées postales sont automatiquement mises en cache en mémoire pour un WP_Query standard (et la requête principale), à moins que vous ne lui indiquiez de ne pas le faire en utilisant le paramètre update_post_meta_cache .

Par conséquent, vous ne devriez pas écrire vos propres requêtes pour cela.

Fonctionnement de la mise en cache méta pour les requêtes normales:

Si le paramètre update_post_meta_cache de WP_Query n'est pas défini sur false, une fois les publications extraites de la base de données, la fonction update_post_caches() est appelée et appelle à son tour update_postmeta_cache() .

La fonction update_postmeta_cache() est un wrapper pour update_meta_cache() et appelle essentiellement un simple SELECT avec tous les identifiants des publications récupérées. Cela lui permettra d'obtenir tout le postmeta, toutes les publications de la requête, et d'enregistrer ces données dans le cache d'objets (à l'aide de wp_cache_add() ).

Lorsque vous faites quelque chose comme get_post_custom() , il vérifie d'abord le cache d'objets. Donc, il ne faut pas faire de requêtes supplémentaires pour obtenir la méta de publication à ce stade. Si vous avez reçu le message dans un WP_Query , alors la méta est déjà en mémoire et elle le prend directement à partir de là.

Les avantages ici sont bien plus nombreux que les requêtes complexes, mais le plus gros avantage provient de l'utilisation du cache d'objets. Si vous utilisez une solution de mise en cache de la mémoire persistante telle que XCache ou memcached, APC ou quelque chose du genre et que vous disposez d'un plug-in pouvant y lier votre cache d'objets (W3 Total Cache, par exemple), alors tout le cache d'objets est stocké en mémoire rapide. déjà. Dans ce cas, des requêtes zéro sont nécessaires pour récupérer vos données. c'est déjà en mémoire. La mise en cache d'objets persistants est géniale à bien des égards.

En d’autres termes, votre requête est probablement chargée plus lentement que si vous utilisiez une requête appropriée et une solution simple de mémoire persistante. Utilisez le WP_Query normal. Epargnez-vous des efforts.

Supplémentaire: update_meta_cache() est intelligent, BTW. Les méta-informations ne seront pas récupérées pour les publications dont les méta-informations sont déjà mises en cache. Il ne fait pas deux fois la même méta, fondamentalement. Super efficace.

Additional additionnel: "Donnez le plus de travail possible à la base de données." ... Non, il s'agit du Web. Des règles différentes s'appliquent. En général, vous voulez toujours donner le moins possible de travail à la base de données, si cela est réalisable. Les bases de données sont lentes ou mal configurées (si vous ne les avez pas configurées spécifiquement, vous pouvez parier que cela est vrai). Souvent, ils sont partagés entre plusieurs sites et surchargés dans une certaine mesure. Vous avez généralement plus de serveurs Web que de bases de données. En général, vous souhaitez extraire les données souhaitées de la base de données aussi rapidement et simplement que possible, puis les trier à l'aide du code côté serveur Web. En règle générale, bien sûr, chaque cas est différent.

    
réponse donnée Otto 24.01.2012 - 06:56
la source
26

Je recommanderais une requête pivot. En utilisant votre exemple:

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
    
réponse donnée Ethan Seifert 24.01.2012 - 06:21
la source
8

J'ai rencontré un cas où je souhaitais également pouvoir récupérer rapidement de nombreux messages avec les méta-informations associées. J'ai besoin de récupérer des messages O (2000).

Je l'ai essayé en utilisant la suggestion d'Otto: exécuter WP_Query :: query pour toutes les publications, puis effectuer une boucle et exécuter get_post_custom pour chaque publication. Cela a pris en moyenne environ 3 secondes .

J'ai ensuite essayé la requête pivot d'Ethan (bien que je n'aime pas avoir à demander manuellement chaque méta_key qui m'intéressait). Il me restait encore à parcourir toutes les publications récupérées pour annuler la sérialisation de la méta_valeur. Cette opération a pris en moyenne environ 1,3 seconde .

J'ai ensuite essayé d'utiliser la fonction GROUP_CONCAT et j'ai trouvé le meilleur résultat. Voici le code:

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

Cela a pris en moyenne 0,7 seconde . C'est environ un quart du temps de la solution WP get_post_custom () et environ la moitié de la solution de requête pivot.

Cela intéressera peut-être quelqu'un.

    
réponse donnée Trevor Mills 07.10.2012 - 17:23
la source
2

Je me suis retrouvé dans une situation où je devais effectuer cette tâche pour créer un document CSV à partir de. J'ai fini par travailler directement avec mysql pour ce faire. Mon code rejoint les tables de méta et les tables de méta pour récupérer les informations de tarification de woocommerce. La solution précédemment publiée nécessitait que j'utilise des alias de table dans SQL pour fonctionner correctement.

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

Attention, woocommerce a créé plus de 300 Ko de lignes dans ma méta-table, elle était donc très grande et donc très lente.

    
réponse donnée Terry Kernan 09.09.2015 - 15:19
la source
0

en utilisant la solution trevor et en la modifiant pour fonctionner avec du SQL imbriqué. Ceci n'est pas testé.

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);
    
réponse donnée Jonathan Joosten 24.09.2014 - 10:01
la source
0

PAS DE VERSION SQL:

Obtenez tous les messages et toutes leurs méta-valeurs (métas) sans SQL:

Supposons que vous ayez une liste d’ID de publication stockés sous la forme d’un tableau d’ID, comme

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

Désormais, obtenir tous les posts et toutes les métas d'une requête est impossible sans au moins un peu de SQL, donc nous devons faire 2 requêtes (encore 2):

1. Obtenez tous les articles (en utilisant WP_Query )

$request = new WP Query([
  'post__in' => $post_ids_list,
  'ignore_sticky_posts' => true, //if you want to ignore the "stickiness"
]);

(N'oubliez pas d'appeler wp_reset_postdata(); si vous effectuez une "loop " après;))

2. Mettre à jour le méta cache

//don't be confused here: "post" means content type (post X user X ...), NOT post type ;)
update_meta_cache('post', $post_ids_list);

Pour obtenir les métadonnées, utilisez simplement le get_post_meta() standard qui, comme l'a souligné @Otto:
examine d'abord le cache:) a>

Remarque: Si vous n'avez pas besoin d'autres données des publications (telles que le titre, le contenu, ...) , vous ne pouvez en faire que deux. : -)

    
réponse donnée jave.web 31.01.2017 - 13:27
la source
-1

J'ai aussi rencontré le problème des champs de méta de valeurs multiples. Le problème est avec WordPress lui-même. Regardez dans wp-includes / meta.php. Recherchez cette ligne:

$where[$k] = ' (' . $where[$k] . $wpdb->prepare( "CAST($alias.meta_value AS {$meta_type}) {$meta_compare} {$meta_compare_string})", $meta_value );

Le problème vient de l'instruction CAST. Dans une requête de méta-valeurs, la variable $ meta_type est définie sur CHAR. Je ne connais pas les détails sur la façon dont CASTing la valeur de CHAR affecte la chaîne sérialisée, mais pour résoudre ce problème, vous pouvez supprimer la conversion afin que le code SQL ressemble à ceci:

$where[$k] = ' (' . $where[$k] . $wpdb->prepare( "$alias.meta_value {$meta_compare} {$meta_compare_string})", $meta_value );

Maintenant, même si cela fonctionne, vous travaillez avec les composants internes de WordPress. Ainsi, d'autres problèmes risquent de se produire, et ce n'est pas une solution permanente, en supposant que vous ayez besoin de mettre à niveau WordPress.

Pour y remédier, je copie le code SQL généré par WordPress pour la méta-requête que je veux, puis j'écris un peu de PHP pour ajouter des instructions AND supplémentaires aux méta_valeurs recherchées et pour utiliser $ wpdb- > get_results ($ sql) pour la sortie finale. Hacky, mais ça marche.

    
réponse donnée Harry Love 23.04.2012 - 21:25
la source

Lire d'autres questions sur les étiquettes