Cách hiệu quả nhất để có được bài viết với postmeta


36

Tôi cần nhận được một loạt các bài đăng với siêu dữ liệu của họ. Tất nhiên, bạn không thể nhận được siêu dữ liệu với truy vấn bài viết tiêu chuẩn, do đó bạn thường phải thực hiện một get_post_custom()cho mỗi bài đăng.

Tôi đang thử với một truy vấn tùy chỉnh, như thế này:

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

Có vẻ để làm việc. Nó tăng lên nếu bạn sử dụng bất kỳ trường meta nào theo cách cho phép nhiều giá trị meta cho nó trên cùng một bài. Tôi không thể nghĩ đến việc tham gia để làm điều đó.

Vì vậy, câu hỏi 1: Có tham gia, truy vấn phụ hoặc bất cứ điều gì, để mang lại các trường meta nhiều giá trị không?

Nhưng câu hỏi 2: Có đáng không? postmetaTôi thêm bao nhiêu phép nối bảng trước khi tiếp cận 2 truy vấn trở nên thích hợp hơn? Tôi có thể lấy tất cả dữ liệu bài đăng trong một truy vấn, sau đó lấy tất cả các postmeta có liên quan trong một truy vấn khác và kết hợp meta với dữ liệu bài đăng trong một tập kết quả trong PHP. Điều đó sẽ kết thúc nhanh hơn một truy vấn SQL phức tạp hơn bao giờ hết, nếu điều đó thậm chí có thể?

Tôi luôn nghĩ: "Hãy giao càng nhiều công việc càng tốt cho cơ sở dữ liệu." Không chăc lăm vê cai nay!


Tôi không chắc nếu bạn thậm chí muốn tham gia. sự kết hợp của get_posts () và get_post_meta () mang lại cho bạn cùng một dữ liệu. Trên thực tế, việc sử dụng các liên kết kém hiệu quả hơn vì bạn có thể đang truy xuất dữ liệu bạn sẽ không sử dụng sau này.
rexposeadas

2
Không đăng bài dữ liệu meta được lưu trữ tự động?
Manny Fleurmond

@rxn, nếu tôi có hàng trăm bài đăng trở lại (chúng là loại bài đăng tùy chỉnh), chắc chắn đó là một tải DB khá nặng get_posts(), sau đó get_post_meta()cho mỗi một trong số đó? @MannyFleurmond, thật khó để tìm thấy thông tin khó về bộ nhớ đệm tích hợp của WP, nhưng AFAIK nó sẽ lưu trữ nội dung theo yêu cầu. Cuộc gọi đến máy chủ để lấy dữ liệu này là cuộc gọi AJAX và tôi không nghĩ rằng bất cứ điều gì khác sẽ lấy thứ đó trước nó.
Steve Taylor

Trên thực tế, tôi sẽ tìm nhiều truy vấn và lưu trữ kết quả. Hóa ra chúng ta không chỉ cần meta bài đăng, bao gồm các trường có nhiều giá trị, chúng ta cũng cần dữ liệu về người dùng được kết nối với bài đăng thông qua các trường meta (hai bộ này), cộng với meta người dùng trên chúng. SQL thuần túy chắc chắn nằm ngoài cửa sổ!
Steve Taylor

Câu trả lời:


58

Thông tin meta bài được tự động lưu vào bộ nhớ trong bộ nhớ cho một tiêu chuẩn WP_Query(và truy vấn chính), trừ khi bạn đặc biệt yêu cầu nó không làm như vậy bằng cách sử dụng update_post_meta_cachetham số.

Do đó, bạn không nên viết các truy vấn của riêng bạn cho việc này.

Cách bộ đệm meta hoạt động cho các truy vấn bình thường:

Nếu update_post_meta_cachetham số đến WP_Querykhông được đặt thành false, thì sau khi các bài đăng được lấy từ DB, thì update_post_caches()hàm sẽ được gọi, lần lượt gọi update_postmeta_cache().

Các update_postmeta_cache()chức năng là một wrapper cho update_meta_cache(), và nó chủ yếu là gọi một cách đơn giản SELECTvới tất cả các ID của các bài viết lấy. Điều này sẽ giúp nó có được tất cả các postmeta, cho tất cả các bài đăng trong truy vấn và lưu dữ liệu đó vào bộ đệm đối tượng (sử dụng wp_cache_add()).

Khi bạn làm một cái gì đó như thế get_post_custom(), nó sẽ kiểm tra bộ đệm đối tượng đó trước. Vì vậy, nó không tạo thêm truy vấn để có được meta bài viết tại thời điểm này. Nếu bạn đã nhận được bài đăng trong một WP_Query, thì meta đã có trong bộ nhớ và nó sẽ được gửi thẳng từ đó.

Ưu điểm ở đây lớn hơn nhiều lần so với thực hiện một truy vấn phức tạp, nhưng lợi thế lớn nhất đến từ việc sử dụng bộ đệm đối tượng. Nếu bạn sử dụng giải pháp bộ nhớ đệm bộ nhớ liên tục như XCache hoặc memcached hoặc APC hoặc một cái gì đó tương tự, và có một plugin có thể buộc bộ đệm đối tượng của bạn với nó (ví dụ W3 Total Cache), thì toàn bộ bộ đệm đối tượng của bạn được lưu trữ trong bộ nhớ nhanh đã Trong trường hợp đó, có không truy vấn cần thiết để lấy dữ liệu của bạn; nó đã có trong bộ nhớ. Bộ nhớ đệm đối tượng liên tục là tuyệt vời trong nhiều khía cạnh.

Nói cách khác, truy vấn của bạn có thể tải và tải chậm hơn so với sử dụng một truy vấn thích hợp và một giải pháp bộ nhớ liên tục đơn giản. Sử dụng bình thường WP_Query. Hãy tiết kiệm cho mình một số nỗ lực.

Bổ sung: update_meta_cache() là thông minh, BTW. Nó sẽ không truy xuất thông tin meta cho các bài đăng đã lưu thông tin meta của họ. Về cơ bản, nó không nhận được meta hai lần. Siêu hiệu quả.

Bổ sung thêm: "Cung cấp càng nhiều công việc càng tốt cho cơ sở dữ liệu." ... Không, đây là web. Quy tắc khác nhau áp dụng. Nói chung, bạn luôn muốn cung cấp càng ít công việc càng tốt cho cơ sở dữ liệu, nếu điều đó là khả thi. Cơ sở dữ liệu được cấu hình chậm hoặc cấu hình kém (nếu bạn không định cấu hình cụ thể, bạn có thể đặt cược tiền tốt rằng điều này là đúng). Thông thường chúng được chia sẻ giữa nhiều trang web và bị quá tải ở một mức độ nào đó. Thông thường bạn có nhiều máy chủ web hơn cơ sở dữ liệu. Nói chung, bạn muốn lấy dữ liệu bạn muốn ra khỏi DB càng nhanh và đơn giản càng tốt, sau đó thực hiện việc sắp xếp nó bằng mã phía máy chủ web. Như một nguyên tắc chung, tất nhiên, các trường hợp khác nhau đều khác nhau.


30

Tôi muốn giới thiệu một truy vấn trục. Sử dụng ví dụ của bạn:

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

Câu trả lời này cần được đánh dấu chính xác.
Lu-ca

Nếu bạn đang tìm kiếm một truy vấn cơ sở dữ liệu thì đây là câu trả lời chính xác
Alex Popov

Truy vấn này đã giảm thời gian của tôi khi tôi đang sử dụng WP_Query từ ~ 25 giây xuống ~ 3 giây. Yêu cầu của tôi là chỉ bắn cái này một lần để không cần bộ nhớ đệm.
Kush

11

Tôi đã gặp một trường hợp mà tôi cũng muốn nhanh chóng lấy lại nhiều bài đăng với thông tin meta liên quan của họ. Tôi cần lấy O (2000) bài viết.

Tôi đã thử nó bằng cách sử dụng đề xuất của Otto - chạy truy vấn WP_Query :: cho tất cả các bài đăng, sau đó lặp qua và chạy get_post_custom cho mỗi bài đăng. Điều này mất trung bình khoảng 3 giây để hoàn thành .

Sau đó, tôi đã thử truy vấn trục của Ethan (mặc dù tôi không muốn phải hỏi thủ công từng meta_key mà tôi quan tâm). Tôi vẫn phải lặp qua tất cả các bài đăng được truy xuất để hủy xác định meta_value. Điều này mất trung bình khoảng 1,3 giây để hoàn thành .

Sau đó tôi đã thử sử dụng hàm GROUP_CONCAT và tìm thấy kết quả tốt nhất. Đây là mã:

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

Điều này mất trung bình 0,7 giây . Đó là khoảng một phần tư thời gian của giải pháp WP get_post_custom () và khoảng một nửa giải pháp truy vấn trục.

Có lẽ điều này sẽ được quan tâm đến một ai đó.


Tôi sẽ quan tâm đến kết quả bạn nhận được với một giải pháp bộ đệm đối tượng liên tục. Bộ đệm đối tượng đôi khi sẽ chậm hơn đối với trường hợp cơ sở, tùy thuộc vào cơ sở dữ liệu và cấu hình của bạn, nhưng kết quả trong thế giới thực với phần lớn máy chủ sẽ cho kết quả rất khác nhau. Bộ nhớ đệm dựa trên bộ nhớ là nhanh chóng lố bịch.
Otto

Này @Otto. Bất kể tôi sử dụng phương pháp nào để lấy dữ liệu, tôi chắc chắn muốn lưu trữ kết quả. Tôi đã thử sử dụng API tạm thời để làm điều đó, nhưng tôi đang gặp vấn đề về bộ nhớ. Chuỗi nối tiếp cho 2000 đối tượng của tôi đồng hồ ở mức ~ 8M và set_transient () không thành công (hết bộ nhớ). Ngoài ra, phải thay đổi cài đặt MySQL max_allowed_packet. Tôi sẽ xem xét bộ nhớ đệm để lưu trữ, nhưng tôi không chắc về hiệu suất ở đó. Có cách nào để lưu trữ vào bộ nhớ vẫn tồn tại trong các yêu cầu không?
Trevor Mills

Có, nếu bạn có bộ nhớ cache bộ nhớ liên tục (XCache, memcached, APC, v.v.) và sử dụng plugin bộ nhớ đệm đối tượng (W3 Total Cache hỗ trợ nhiều loại bộ nhớ đệm), thì nó sẽ lưu trữ tất cả bộ đệm của đối tượng trong bộ nhớ, cung cấp cho bạn tăng tốc nhiều lần của khá nhiều thứ.
Otto

Tôi đang trả lại 6000 mục để sử dụng trong sơ đồ lọc js xương sống / gạch dưới. Điều này đã lấy một truy vấn tùy chỉnh 6s mà tôi thậm chí không thể chạy như một WP_Query vì nó đã hết thời gian và biến nó thành truy vấn 2 giây. Mặc dù mảng_map làm chậm nó xuống một chút ...
Jake

Có bất kỳ hỗ trợ nào để xây dựng hỗ trợ hiệu suất cao để trả về tất cả dữ liệu meta trong WP_Query không?
atwellpub

2

Tôi thấy mình trong một tình huống mà tôi cần phải thực hiện nhiệm vụ này để cuối cùng tạo ra một tài liệu CSV, cuối cùng tôi đã làm việc trực tiếp với mysql để thực hiện điều này. Mã của tôi tham gia các bảng bài đăng và meta để lấy thông tin về giá thương mại điện tử, giải pháp được đăng trước đó yêu cầu tôi sử dụng các bí danh bảng trong sql để hoạt động chính xác.

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

Mặc dù được cảnh báo, thương mại điện tử đã tạo ra 300K + hàng trong bảng meta của tôi, vì vậy nó rất lớn và do đó rất chậm.


1

PHIÊN BẢN SQL KHÔNG:

Nhận tất cả các bài đăng và tất cả các giá trị meta của chúng (metas) không có SQL:

Giả sử bạn có một danh sách ID bài đăng được lưu trữ dưới dạng một mảng ID, đại loại như

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

Bây giờ không thể nhận được tất cả các bài đăng và tất cả các metas trong 1 truy vấn mà không sử dụng ít nhất một chút SQL, vì vậy chúng tôi phải thực hiện 2 truy vấn (vẫn chỉ là 2):

1. Nhận tất cả các bài đăng (sử dụng WP_Query )

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

(Đừng quên gọi wp_reset_postdata();nếu bạn đang thực hiện "vòng lặp" sau đó;))

2. Cập nhật bộ đệm meta

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

Để có được dữ liệu meta, chỉ cần sử dụng tiêu chuẩn get_post_meta(), như @Otto đã chỉ ra:
trước tiên hãy nhìn vào bộ đệm :)

Lưu ý: Nếu bạn không thực sự cần dữ liệu khác từ các bài đăng (như tiêu đề, nội dung, ...), bạn có thể làm chỉ 2. :-)


0

sử dụng trevor mẫu giải pháp và sửa đổi nó để làm việc với SQL lồng nhau. Điều này không được thử nghiệm.

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

Tôi cũng gặp vấn đề về nhiều trường meta. Vấn đề là với chính WordPress. Tìm trong wp-gộp / meta.php. Tìm dòng này:

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

Vấn đề là với câu lệnh CAST. Trong truy vấn cho các giá trị meta, biến $ meta_type được đặt thành CHAR. Tôi không biết chi tiết về cách CASTing giá trị thành CHAR ảnh hưởng đến chuỗi được xê-ri hóa, nhưng để khắc phục nó, bạn có thể loại bỏ các biểu tượng để SQL trông như thế này:

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

Bây giờ, mặc dù điều đó có hiệu quả, nhưng bạn đang làm quen với các phần bên trong WordPress, vì vậy những thứ khác có thể bị hỏng và đó không phải là bản sửa lỗi vĩnh viễn, giả sử bạn sẽ cần nâng cấp WordPress.

Cách tôi đã sửa là sao chép SQL do WordPress tạo cho truy vấn meta tôi muốn và sau đó viết một số PHP để giải quyết các câu lệnh AND bổ sung cho meta_values ​​tôi đang tìm và sử dụng $ wpdb-> get_results ($ sql ) cho đầu ra cuối cùng. Hacky, nhưng nó hoạt động.


Tôi đã không thử nó, nhưng tận dụng get_meta_sqlbộ lọc theo dòng này tất nhiên sẽ thích hợp hơn để hack mã lõi.
Steve Taylor
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.