วิธีที่มีประสิทธิภาพที่สุดในการรับโพสต์ด้วย postmeta


35

ฉันต้องได้รับข้อความมากมายจากเมตาดาต้า แน่นอนว่าคุณไม่สามารถรับเมทาดาทาที่มีแบบสอบถามโพสต์มาตรฐานดังนั้นโดยทั่วไปคุณต้องทำ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 คิวรีจะดีกว่า ฉันสามารถดึงข้อมูลโพสต์ทั้งหมดในแบบสอบถามหนึ่งแล้วคว้า postmeta ที่เกี่ยวข้องทั้งหมดในอีกแบบสอบถามและรวมเมตากับข้อมูลโพสต์ในชุดผลลัพธ์หนึ่งใน PHP ว่าจะเร็วกว่าการสืบค้น SQL ที่ซับซ้อนยิ่งขึ้นถ้าเป็นไปได้หรือไม่

ฉันมักจะคิดว่า "ให้ฐานข้อมูลมากที่สุดเท่าที่จะเป็นไปได้" ไม่แน่ใจในอันนี้!


ฉันไม่แน่ใจว่าคุณต้องการที่จะรวม การรวมกันของ get_posts () และ get_post_meta () ให้ข้อมูลที่เหมือนกันกลับมาให้คุณ อันที่จริงแล้วมันมีประสิทธิภาพน้อยกว่าเมื่อใช้การรวมเนื่องจากคุณอาจดึงข้อมูลที่คุณจะไม่ใช้ในภายหลัง
rexposadas

2
ยังไม่ได้โพสต์ข้อมูลเมตาที่แคชไว้โดยอัตโนมัติหรือไม่
Manny Fleurmond

@rxn ถ้าฉันมีหลายร้อยโพสต์กลับมา (พวกเขาเป็นประเภทโพสต์ที่กำหนดเอง) แน่นอนว่ามันเป็นภาระ DB หนักไปget_posts()แล้วget_post_meta()สำหรับทุกคนหรือไม่ @ DannyFleurmond มันยากที่จะหาข้อมูลที่ยากในการแคชในตัวของ WP แต่ AFAIK มันจะแคชสิ่งต่าง ๆ ตามคำขอ การโทรไปยังเซิร์ฟเวอร์เพื่อรับข้อมูลนี้เป็นการโทร AJAX และฉันไม่คิดว่าจะมีอะไรอื่นมาหยิบอีกต่อไป
Steve Taylor

ที่จริงแล้วฉันจะสอบถามหลายรายการและแคชผลลัพธ์ ปรากฎว่าเราไม่เพียงต้องการโพสต์เมตาเท่านั้นรวมถึงเขตข้อมูลที่มีหลายค่าเรายังต้องการข้อมูลเกี่ยวกับผู้ใช้ที่เชื่อมต่อกับโพสต์ผ่านเขตข้อมูลเมตา (ชุดสองชุด) รวมถึงเมตาผู้ใช้ Pure SQL อยู่นอกหน้าต่างแน่นอน!
Steve Taylor

คำตอบ:


58

ข้อมูลการโพสต์เมตาถูกแคชไว้ในหน่วยความจำโดยอัตโนมัติสำหรับมาตรฐานWP_Query(และแบบสอบถามหลัก) เว้นแต่คุณจะบอกเป็นพิเศษว่าไม่ให้ทำเช่นนั้นโดยใช้update_post_meta_cacheพารามิเตอร์

ดังนั้นคุณไม่ควรเขียนข้อความค้นหาของคุณเอง

วิธีการทำงานของเมตาแคชสำหรับการสืบค้นปกติ:

หากupdate_post_meta_cacheพารามิเตอร์ไปยังWP_Queryไม่ได้ตั้งค่าเป็นเท็จหลังจากนั้นจะมีการดึงโพสต์ออกจากฐานข้อมูลจากนั้น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 ทั้งหมด) จากนั้นแคชวัตถุทั้งหมดของคุณจะถูกเก็บไว้ในหน่วยความจำเร็ว แล้ว. ในกรณีนี้มีการค้นหาที่จำเป็นเป็นศูนย์เพื่อดึงข้อมูลของคุณ มันมีอยู่แล้วในหน่วยความจำ การแคชวัตถุถาวรนั้นยอดเยี่ยมในหลาย ๆ ด้าน

กล่าวอีกนัยหนึ่งแบบสอบถามของคุณอาจโหลดและโหลดช้ากว่าการใช้แบบสอบถามที่เหมาะสมและโซลูชันหน่วยความจำถาวรอย่างง่าย WP_Queryใช้ปกติ ช่วยตัวเองด้วยความพยายาม

เพิ่มเติม: update_meta_cache()ฉลาด BTW มันจะไม่ดึงข้อมูลเมตาสำหรับโพสต์ที่มีเมตาดาต้าแคชอยู่แล้ว ไม่ได้รับเมตาเดียวกันสองครั้งโดยทั่วไป มีประสิทธิภาพสูงสุด

เพิ่มเติมเพิ่มเติม: "มอบฐานข้อมูลให้มากที่สุด" ... ไม่นี่คือเว็บ ใช้กฎที่แตกต่างกัน โดยทั่วไปแล้วคุณต้องการให้ฐานข้อมูลน้อยที่สุดเท่าที่จะทำได้หากเป็นไปได้ ฐานข้อมูลช้าหรือมีการกำหนดค่าไม่ดี (หากคุณไม่ได้กำหนดค่าไว้เป็นพิเศษคุณสามารถเดิมพันเงินที่ดีซึ่งเป็นจริงได้) บ่อยครั้งที่พวกเขาจะใช้ร่วมกันในหลาย ๆ ไซต์และมากเกินไปในระดับหนึ่ง โดยปกติคุณจะมีเว็บเซิร์ฟเวอร์มากกว่าฐานข้อมูล โดยทั่วไปคุณต้องการเพียงดึงข้อมูลที่คุณต้องการออกจากฐานข้อมูลอย่างรวดเร็วและง่ายดายที่สุดจากนั้นทำการคัดแยกออกโดยใช้รหัสด้านเว็บเซิร์ฟเวอร์ ตามหลักการทั่วไปของหลักสูตรกรณีที่แตกต่างกันแตกต่างกันทั้งหมด


30

ฉันจะแนะนำแบบสอบถาม Pivot ใช้ตัวอย่างของคุณ:

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

คำตอบนี้ควรถูกทำเครื่องหมายว่าถูกต้อง
ลุค

หากคุณกำลังมองหาการสืบค้นฐานข้อมูลนี่เป็นคำตอบที่ถูกต้อง
Alex Popov

แบบสอบถามนี้ลดเวลาของฉันเมื่อฉันใช้ WP_Query จาก ~ 25 วินาทีถึง ~ 3 วินาที ความต้องการของฉันคือยิงสิ่งนี้เพียงครั้งเดียวเท่านั้นดังนั้นจึงไม่ต้องการแคช
Kush

10

ฉันเจอกรณีที่ฉันต้องการดึงโพสต์จำนวนมากอย่างรวดเร็วด้วยข้อมูลเมตาที่เกี่ยวข้อง ฉันต้องดึงข้อมูลโพสต์ O (2000)

ฉันลองใช้ข้อเสนอแนะของ Otto - การรัน WP_Query :: query สำหรับโพสต์ทั้งหมดจากนั้นวนลูปและเรียกใช้ get_post_custom สำหรับแต่ละโพสต์ นี้ใช้เวลาโดยเฉลี่ยประมาณ 3 วินาทีให้เสร็จสมบูรณ์

จากนั้นฉันลองใช้เครื่องมือค้นหา pivot ของอีธาน (แม้ว่าฉันไม่ต้องการถาม 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 () และประมาณครึ่งหนึ่งของโซลูชันแบบสอบถาม pivot

บางทีนี่อาจเป็นที่สนใจของใครบางคน


ฉันสนใจสิ่งที่ผลลัพธ์ที่คุณได้รับจากโซลูชันแคชวัตถุแบบถาวร แคชวัตถุจะช้าลงสำหรับกรณีพื้นฐานบางครั้งขึ้นอยู่กับฐานข้อมูลและการกำหนดค่าของคุณ แต่ผลลัพธ์ในโลกแห่งความจริงที่มีโฮสต์ส่วนใหญ่จะให้ผลลัพธ์ที่แตกต่างกันอย่างกว้างขวาง การแคชตามหน่วยความจำนั้นรวดเร็วมาก
อ็อตโต

เฮ้ @Otto ไม่ว่าฉันจะใช้วิธีใดในการรับข้อมูลฉันต้องการแคชผลลัพธ์อย่างแน่นอน ฉันพยายามใช้ transient API เพื่อทำมัน แต่ฉันกำลังตีปัญหาหน่วยความจำ สตริงที่ต่อเนื่องกันสำหรับนาฬิกาวัตถุ 2000 รายการของฉันที่ ~ 8M และ set_transient () ล้มเหลว (หน่วยความจำหมด) นอกจากนี้ยังต้องเปลี่ยนการตั้งค่า MySQL max_allowed_packet ฉันจะดูแคชเพื่อไฟล์ แต่ฉันยังไม่แน่ใจเกี่ยวกับประสิทธิภาพที่มี มีวิธีแคชในหน่วยความจำที่ยังคงอยู่ระหว่างการร้องขอหรือไม่?
Trevor Mills

ใช่ถ้าคุณมีแคชหน่วยความจำถาวร (XCache, memcached, APC และอื่น ๆ ) และใช้ปลั๊กอินแคชวัตถุ (W3 Total Cache รองรับแคชหน่วยความจำหลายประเภท) จากนั้นจะเก็บแคชวัตถุทั้งหมดไว้ในหน่วยความจำ เพิ่มความเร็วได้หลายเท่าในทุกสิ่ง
อ็อตโต

ฉันส่งคืน 6,000 รายการเพื่อใช้ในโครงร่างการกรองแบ็คโบน / ขีดล่าง นี่ใช้คิวรีแบบกำหนดเอง 6s ที่ฉันไม่สามารถเรียกใช้ในฐานะ WP_Query เพราะหมดเวลาและทำให้เป็นคิวรี 2s แม้ว่า array_map จะทำให้มันช้าลงเล็กน้อย ...
Jake

มีการสนับสนุนสำหรับการสร้างการสนับสนุนที่มีประสิทธิภาพสูงเพื่อส่งคืนข้อมูลเมตาทั้งหมดภายใน WP_Query หรือไม่
atwellpub

2

ฉันพบว่าตัวเองอยู่ในสถานการณ์ที่ฉันต้องทำงานนี้เพื่อสร้างเอกสาร 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 + ในตารางเมตาของฉันดังนั้นมันจึงมีขนาดใหญ่มากและช้ามาก


1

ไม่มี SQL เวอร์ชัน:

รับโพสต์ทั้งหมดและค่าเมตาทั้งหมดของพวกเขา (เมตาดาต้า) โดยไม่มี SQL:

สมมติว่าคุณมีรายการรหัสโพสต์ที่จัดเก็บเป็นอาร์เรย์ของรหัสบางอย่างเช่น

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

ตอนนี้รับโพสต์ทั้งหมดและเมตาทั้งหมดใน 1 ข้อความค้นหาเป็นไปไม่ได้หากไม่ใช้ SQL อย่างน้อยสักหน่อยดังนั้นเราต้องทำ 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 :-)


0

ใช้แบบฟอร์มโซลูชันเทรเวอร์และปรับเปลี่ยนให้ทำงานกับ 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);

-1

ฉันวิ่งเข้าไปในปัญหาเขตข้อมูลเมตาหลายค่าเช่นกัน ปัญหาเกิดขึ้นกับตัว WordPress เอง ดูใน wp-include / 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 ฉันไม่ทราบรายละเอียดว่า CASTing ค่าต่อ CHAR มีผลต่อสตริงแบบอนุกรมอย่างไร แต่หากต้องการแก้ไขคุณสามารถลบ cast ออกเพื่อให้ SQL มีลักษณะดังนี้:

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

ตอนนี้แม้ว่าจะได้ผลก็ตามคุณกำลังล้อเล่นกับ WordPress ภายในดังนั้นสิ่งอื่น ๆ อาจแตกหักและไม่ใช่การแก้ไขที่ถาวรถ้าคุณจะต้องอัปเกรด WordPress

วิธีที่ฉันแก้ไขคือการคัดลอก SQL ที่สร้างโดย WordPress สำหรับการสืบค้น meta ที่ฉันต้องการและจากนั้นเขียน PHP บางอย่างเพื่อแก้ไขเพิ่มเติมและงบสำหรับ meta_values ​​ที่ฉันกำลังค้นหาและใช้ $ wpdb-> get_results ($ sql ) สำหรับผลลัพธ์สุดท้าย แฮ็ก แต่มันใช้งานได้


ฉันไม่ได้ลอง แต่ใช้ประโยชน์จากget_meta_sqlตัวกรองที่ตามหลังบรรทัดนี้แน่นอนว่าจะดีกว่าในการแฮ็กรหัสหลัก
Steve Taylor
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.