Postmeta ile mesaj almak için en etkili yol


35

Meta verileriyle birlikte bir sürü mesaj almam gerekiyor. Tabii ki standart bir get_post_custom()yazı sorgusu ile meta veri elde edemezsiniz, bu nedenle genellikle her yazı için bir yapmanız gerekir .

Bunun gibi bir özel sorgu ile çalışıyorum:

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

İş gibi görünüyor. Bu meta alanlarından herhangi birini aynı yayında birden fazla meta değere izin verecek şekilde kullanırsanız açılır. Bunu yapmak için bir birleşme düşünemiyorum.

Öyleyse, soru 1: Çok değerli meta alanlarını getirmek için bir birleştirme, alt sorgu veya herhangi bir şey var mı?

Fakat 2. soru: Buna değer mi? postmeta2-sorgulama yaklaşımı tercih edilmeden önce kaç masa birleşimi eklerim? Tüm posta verilerini bir sorguda yakalayabilir, ardından ilgili tüm postaları bir başkasına alabilirim ve meta'yu post verisiyle bir sonuç setinde PHP'de birleştiririm. Bu mümkün olsa bile, her zamankinden daha karmaşık bir SQL sorgusundan daha hızlı sonuç verir mi?

Her zaman, "Veritabanına mümkün olduğunca fazla iş ver" diye düşünüyorum. Bundan emin değilim!


Katılmayı yapmak istediğinden bile emin değilim. get_posts () ve get_post_meta () kombinasyonu size aynı verileri geri verir. Aslında, daha sonra kullanmayacağınız verileri alıyor olacağınız için katılımları kullanmak daha az etkilidir.
rexposadas

2
Post meta verileri yine de otomatik olarak önbelleğe alınmıyor mu?
Manny Fleurmond

Geri dönmeyi birkaç yüz mesajları varsa @rxn, mutlaka oldukça ağır bir DB yük tebrik ediyor (onlar özel yazı alanı konum) get_posts()daha sonra, get_post_meta()bu her biri için? @MannyFleurmond, WP'nin yerleşik önbelleğe alma hakkında zor bilgi bulmak zor, ancak AFAIK istek başına malzeme önbelleğe alır. Bu verileri almak için sunucuya yapılan çağrı bir AJAX çağrısıdır ve başka hiçbir şeyin ondan önce bir şeyler alacağını sanmıyorum.
Steve Taylor

Aslında, birden çok sorgulamaya gidiyorum ve sonuçları önbelleğe alıyorum. Birden fazla değeri olan alanlar da dahil olmak üzere yalnızca post metaya ihtiyacımız olmadığı, ayrıca meta alanlar (bu iki takım kümeler) üzerinden mesajlara bağlı kullanıcılara ve bunlara ilişkin kullanıcı metalarına da ihtiyacımız olduğu ortaya çıktı. Saf SQL kesinlikle pencerenin dışında!
Steve Taylor

Yanıtlar:


58

Post meta bilgileri, bir parametre WP_Query(ve ana sorgu) için özel olarak bellekte önbelleğe alınır , aksi takdirde bunu update_post_meta_cacheparametre kullanarak yapmazsınız.

Bu nedenle, bunun için kendi sorularınızı yazmamalısınız.

Meta önbellekleme normal sorgular için nasıl çalışır:

Eğer update_post_meta_cacheparametre WP_Queryfalse olarak ayarlanır değil, o mesajlar DB'den alındıktan sonra, o update_post_caches()işlev hangi dönüş çağrıları, adı verilecek update_postmeta_cache().

update_postmeta_cache()Fonksiyon için bir sarıcı update_meta_cache()ve aslında bir basit çağıran SELECTtüm kimlikleri alınan mesajların ile. Bu, sorgudaki tüm postalar için tüm postaları alacak ve bu verileri nesne önbelleğinde (kullanarak wp_cache_add()) kaydedecektir .

Gibi bir şey yaptığınızda get_post_custom(), önce o nesne önbelleğini kontrol eder. Bu nedenle meta göndermeyi bu noktada almak için fazladan sorgulama yapmıyoruz. Gönderiyi a içine aldıysanız WP_Query, meta zaten bellektedir ve doğrudan oradan alır.

Buradaki avantajlar karmaşık bir sorgu yapmaktan çok daha fazladır, ancak en büyük avantaj nesne önbelleğini kullanmaktan kaynaklanmaktadır. XCache veya memcached veya APC gibi kalıcı bir bellek önbellekleme çözümü kullanıyorsanız ve buna benzer bir şey kullanıyorsanız ve nesne önbelleğinizi buna bağlayabilecek bir eklenti varsa (örneğin, W3 Toplam Önbellek), tüm nesne önbelleğiniz hızlı bellekte saklanır. zaten. Bu durumda, verilerinizi almak için gerekli sıfır sorgu vardır ; zaten bellekte. Kalıcı nesne önbelleklemesi birçok bakımdan harika.

Başka bir deyişle, sorgunuz muhtemelen düzgün bir sorgu ve basit bir kalıcı bellek çözümü kullanmaktan daha yavaş yüklenir ve yüklenir. Normal kullanın WP_Query. Kendine biraz çaba göster.

Ek: update_meta_cache() akıllı, BTW. Zaten meta bilgileri önbelleğe alınmış olan gönderiler için meta bilgileri almaz. Temelde iki kez aynı meta alamaz. Süper verimli.

Ek ek: "Veritabanına mümkün olduğunca çok iş ver." ... Hayır, bu web. Farklı kurallar geçerlidir. Genel olarak, eğer uygunsa, her zaman veritabanına mümkün olduğunca az iş vermek istersiniz . Veritabanları yavaş veya kötü yapılandırılmış (özel olarak yapılandırmadıysanız, bunun doğru olduğu konusunda iyi para yatırabilirsiniz). Genellikle birçok site arasında paylaşılır ve bir dereceye kadar aşırı yüklenir. Genellikle veritabanlarından daha fazla web sunucunuz olur. Genel olarak, DB'den istediğiniz verileri olabildiğince hızlı ve basit bir şekilde elde etmek, ardından web sunucusu tarafı kodunu kullanarak sıralamayı yapmak istersiniz. Genel bir prensip olarak, elbette, farklı durumların hepsi farklıdır.


30

Bir özet sorgusu tavsiye ederim. Örneğinizi kullanarak:

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

Bu cevap doğru olarak işaretlenmelidir.
Luka

Eğer bir veritabanı sorgusu arıyorsanız, bu doğru cevaptır
Alex Popov

Bu sorgu WP_Query kullanırken ~ 25 sn'den ~ 3 sn'ye kadar zamanımı düşürdü. İhtiyacım, bunu sadece bir kez ateşlemekti, böylece önbelleklemeye gerek yoktu.
Kush

10

İlişkilendirilmiş meta bilgileri ile birlikte çok sayıda gönderiyi hızlıca almak istediğim bir durumla karşılaştım. O (2000) yayınları almam gerekiyor.

Otto'nun önerisini kullanarak denedim - tüm yazılar için WP_Query :: query, sonra da her yazı için loop ve get_post_custom'i çalıştırıyordum. Bu işlemin tamamlanması ortalama 3 saniye sürmüştür .

Sonra Ethan'ın pivot sorgusunu denedim (ilgilendiğim her meta_key için elle sormak istememize rağmen). Meta_değerini sıralamak için hala tüm alınan gönderiler arasında dolaşmam gerekti. Bu işlemin tamamlanması ortalama olarak 1,3 saniye sürmüştür .

GROUP_CONCAT işlevini kullanmayı denedim ve en iyi sonucu buldum. İşte kod:

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

Bu ortalama 0,7 saniye sürdü . Bu, WP get_post_custom () çözümünün yaklaşık dörtte biri ve pivot sorgu çözümünün yaklaşık yarısı kadardır.

Belki bu biri için ilgi çekici olacaktır.


Kalıcı bir nesne önbellek çözümü ile elde ettiğiniz sonuçlarla ilgilenirim. Nesne önbelleği bazen veritabanınıza ve yapılandırmanıza bağlı olarak temel durum için daha yavaş olacaktır, ancak çoğu ana makineyle birlikte gerçek dünya sonuçları, çok çeşitli sonuçlar verecektir. Bellek tabanlı önbellekleme, gülünç hızlı.
Otto,

Hey @Otto. Verileri elde etmek için hangi yöntemi kullandığımdan bağımsız olarak, sonucu kesinlikle önbelleğe almak istiyorum. Geçici API'yi yapmak için kullanmaya çalıştım, ancak bellek sorunlarıyla karşılaşıyorum. Benim 2000 nesneler için seri hale getirilmiş dize ~ 8M'deki saatler ve set_transient () başarısız olur (bellek bitti). Ayrıca, max_allowed_packet MySQL ayarını değiştirmek zorundasınız. Dosyaya önbelleğe almak için bakacağım, ancak orada performans için henüz emin değilim. İstekler arasında süren belleği önbelleğe almanın bir yolu var mı?
Trevor Mills

Evet, kalıcı bir bellek önbelleğiniz varsa (XCache, memcached, APC, vb.) Ve bir nesne önbellek eklentisi kullanıyorsanız (W3 Total Cache birçok bellek önbellek türünü destekler), o zaman tüm nesne önbelleğini bellekte depolar ve size bir hemen hemen her şeyin çok katlı hızlanması.
Otto

Omurga / alt çizgi js filtreleme düzeninde kullanmak için 6000 öğe iade ediyorum. Bu zaman aşımına uğradı ve 2s sorgu yaptı çünkü WP_Query olarak bile çalıştıramadım 6s özel bir sorgu aldı. Array_map biraz yavaşladı rağmen ...
Jake

Bir WP_Query içindeki tüm meta verileri döndürmek için yüksek performanslı destek oluşturma konusunda herhangi bir destek var mı?
atwellpub

2

Sonunda bir CSV belgesi oluşturmak için bu görevi yapmam gereken bir durumda buldum, bunu yapmak için doğrudan mysql ile çalışmaya başladım. Kodum, woocommerce fiyat bilgilerini almak için posta ve meta tablolarına katılır; daha önce yayınlanan çözüm, sql'deki tablo diğer adlarını düzgün çalışması için kullanmam gerekirdi.

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

Ancak, uyarılmayın, woocommerce meta tablomda 300K + satır oluşturdu, bu yüzden çok büyüktü ve bu nedenle çok yavaştı.


1

SQL SÜRÜM YOK:

Bütün mesajları ve meta değerlerini (metas) SQL olmadan al:

Diyelim ki bir dizi kimlik gibi depolanan posta kimlikleri listeniz var.

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

Şimdi tüm sorguları ve tüm sorguları 1 sorguda almak, en az bir miktar SQL kullanmadan mümkün değildir, bu yüzden 2 sorgu yapmalıyız (hala sadece 2):

1. Tüm mesajları al ( WP_Query kullanarak )

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

(Daha sonra "döngü"wp_reset_postdata(); yapıyorsanız aramayı unutmayın ;)

2. Meta önbelleği güncelle

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

Meta verileri almak için, sadece get_post_meta(), @Otto'nun belirttiği gibi: önce önbelleğe bakan standardı kullanın
:)

Not: Gönderilerden gelen diğer verilere (başlık, içerik, ... gibi) gerçekten ihtiyacınız yoksa, sadece 2 yapabilirsiniz. :-)


0

trevor çözüm formunu kullanarak ve onu yuvalanmış SQL ile çalışacak şekilde değiştirmek. Bu test edilmedi.

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

-1

Çoklu değer meta alanları problemiyle de karşılaştım. Sorun WordPress'in kendisinde. Wp-include / meta.php dosyasına bakın. Bu çizgiyi arayın:

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

Sorun CAST ifadesindedir. Meta değerleri sorgusundaki $ meta_type değişkeni CHAR olarak ayarlanmıştır. Değerin CHAR'a geçirilmesinin seri hale getirilmiş dizgiyi nasıl etkilediğine dair ayrıntıları bilmiyorum, ancak düzeltmek için, dökümcüyü kaldırarak SQL şöyle görünür:

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

Şimdi, bu işe yaramasına rağmen, WordPress'in içindekilerle dalga geçiyorsunuz, bu nedenle başka şeyler bozulabilir ve WordPress'i yükseltmeniz gerekebileceğini varsaymak kalıcı bir düzeltme değildir.

Düzelttiğim yöntem, istediğim meta sorgusu için WordPress tarafından oluşturulan SQL'i kopyalamak ve daha sonra aradığım meta_values ​​için ekstra AND ifadeleriyle uğraşmak üzere PHP yazmak ve $ wpdb-> get_results ($ sql kullanmak. ) son çıktı için. Hacky, ama işe yarıyor.


Denemedim, ancak get_meta_sqlbu satırı izleyen filtreyi kullanmak elbette çekirdek kodu kırmak için tercih edilir.
Steve Taylor
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.