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 : 그만한 가치가 있습니까? postmeta2 쿼리 접근 방식이 선호되기 전에 몇 개의 테이블 조인을 추가합니까? 하나의 쿼리에서 모든 게시물 데이터를 가져온 다음 다른 관련 postmeta를 다른 쿼리에서 가져 와서 메타의 게시물 데이터와 PHP의 한 결과 집합을 결합 할 수 있습니다. 가능하다면 더욱 복잡한 단일 SQL 쿼리보다 더 빠를까요?

저는 항상 "데이터베이스에 최대한 많은 작업을 수행하십시오"라고 생각합니다. 확실하지 않습니다!


당신이 조인을하고 싶은지 확실하지 않습니다. get_posts ()와 get_post_meta ()의 조합은 동일한 데이터를 다시 제공합니다. 실제로 나중에 사용하지 않을 데이터를 검색 할 수 있으므로 조인을 사용하는 것이 효율성이 떨어집니다.
rexposadas 2012 년

2
어쨌든 자동으로 캐시 된 메타 데이터를 게시하지 않습니까?
매니 Fleurmond

내가 돌아 오는 수백 개의 게시물이있는 경우 @rxn, 확실히 꽤 무거운의 DB 부하에있어, (그들은 사용자 정의 포스트 유형있어) get_posts()그 다음, get_post_meta()그 모든 하나? @MannyFleurmond, WP의 내장 캐싱에 대한 하드 정보를 찾기는 어렵지만 AFAIK는 요청마다 물건을 캐시합니다. 이 데이터를 가져 오기 위해 서버에 전화하는 것은 AJAX 호출이며, 그 전에는 물건을 잡을 것이라고 생각하지 않습니다.
Steve Taylor

실제로 여러 쿼리를 수행하고 결과를 캐싱합니다. 여러 값을 가진 필드를 포함하여 게시물 메타가 필요 할뿐만 아니라 메타 필드 (이들 중 두 세트)를 통해 게시물에 연결된 사용자의 데이터와 사용자 메타가 필요합니다. 순수한 SQL은 확실히 창밖입니다!
Steve Taylor

답변:


58

매개 변수 WP_Query를 사용하여 그렇게하지 않도록 특별히 지시하지 않는 한, 포스트 메타 정보는 표준 (및 기본 쿼리)을 위해 메모리에 자동으로 캐시됩니다 update_post_meta_cache.

따라서 이에 대한 자체 쿼리를 작성해서는 안됩니다.

메타 캐싱이 일반 쿼리에서 작동하는 방식 :

에 대한 update_post_meta_cache매개 변수가 WP_Queryfalse로 설정되지 않은 경우 DB에서 게시물을 검색 한 후 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)이 있으면 전체 객체 캐시가 빠른 메모리에 저장됩니다 이미. 이 경우에, 거기 제로 데이터를 검색하는 데 필요한 쿼리를; 이미 메모리에 있습니다. 지속적인 객체 캐싱은 여러 측면에서 훌륭합니다.

다시 말해, 쿼리는 적절한 쿼리와 간단한 영구 메모리 솔루션을 사용하는 것보다로드 및로드 속도가 느릴 수 있습니다. normal을 사용하십시오 WP_Query. 노력을 절약하십시오.

추가 : update_meta_cache() 똑똑하다, BTW. 메타 정보가 이미 캐시 된 게시물의 메타 정보는 검색하지 않습니다. 기본적으로 동일한 메타를 두 번 얻지 않습니다. 매우 효율적입니다.

추가 추가 사항 : "데이터베이스에 최대한 많은 작업을 수행하십시오."... 아니요, 웹입니다. 다른 규칙이 적용됩니다. 일반적 으로 가능한 경우 데이터베이스에 가능한 한 적은 작업 을 제공하려고합니다 . 데이터베이스가 느리게 구성되거나 잘못 구성되었습니다 (구체적으로 구성하지 않은 경우, 이것이 사실이라고 돈을 걸 수 있습니다). 종종 그들은 많은 사이트들 사이에서 공유되고 어느 정도 오버로드됩니다. 일반적으로 데이터베이스보다 많은 웹 서버가 있습니다. 일반적으로 DB에서 원하는 데이터를 가능한 한 빠르고 간단하게 얻은 다음 웹 서버 측 코드를 사용하여 정렬하십시오. 물론 일반적인 원칙으로 다른 사례는 모두 다릅니다.


30

피벗 쿼리를 권장합니다. 귀하의 예를 사용하여 :

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

이 답변은 올바른 것으로 표시되어야합니다.
Luke

데이터베이스 쿼리를 찾고 있다면 이것이 정답입니다
Alex Popov

이 쿼리는 WP_Query를 사용하는 시간을 ~ 25 초에서 ~ 3 초로 줄였습니다. 내 요구 사항은 이것을 한 번만 실행하여 캐싱이 필요하지 않은 것입니다.
Kush

10

관련 메타 정보가 포함 된 많은 게시물을 신속하게 검색하려는 경우가 있습니다. O (2000) 게시물을 검색해야합니다.

나는 모든 게시물에 대해 WP_Query :: query를 실행 한 다음 각 게시물에 대해 get_post_custom을 반복 실행하여 Otto의 제안을 사용하여 시도했습니다. 완료하는 데 평균 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 () 솔루션 시간의 약 4 분의 1과 피벗 쿼리 솔루션의 약 절반입니다.

어쩌면 이것은 누군가에게 관심이있을 것입니다.


영구 객체 캐시 솔루션으로 얻는 결과에 관심이 있습니다. 데이터베이스와 구성에 따라 기본 경우 개체 캐시가 느려질 수 있지만 대부분의 호스트에서 실제 결과는 매우 다양한 결과를 제공합니다. 메모리 기반 캐싱은 엄청나게 빠릅니다.
Otto

안녕하세요 @Otto. 데이터를 얻는 데 사용하는 방법에 관계없이 결과를 캐시하고 싶습니다. 임시 API를 사용하여 시도했지만 메모리 문제가 발생했습니다. 내 2000 객체의 직렬화 된 문자열은 ~ 8M에서 클럭하고 set_transient ()가 실패합니다 (메모리가 소진되었습니다). 또한 max_allowed_packet MySQL 설정을 변경해야합니다. 파일로 캐싱하는 방법을 살펴볼 것입니다. 그러나 아직 성능이 확실하지 않습니다. 여러 요청에 걸쳐 지속되는 메모리에 캐시하는 방법이 있습니까?
Trevor Mills

예. 영구 메모리 캐시 (XCache, memcached, APC 등)가 있고 객체 캐싱 플러그인 (W3 Total Cache는 여러 유형의 메모리 캐시를 지원함)을 사용하는 경우 모든 객체 캐시를 메모리에 저장하여 거의 모든 것의 많은 배속.
오토

백본 / 밑줄 js 필터링 체계에 사용할 6000 개의 항목을 반환합니다. 이것은 시간 초과되어 WP_Query로 실행할 수없는 6 초 사용자 정의 쿼리를 가져 와서 2 초 쿼리로 만들었습니다. array_map이 상당히 느려졌지만 ...
Jake

WP_Query 내에서 모든 메타 데이터를 리턴하기위한 고성능 지원을 빌드하기위한 지원이 있습니까?
atwellpub

2

궁극적으로 CSV 문서를 만들기 위해이 작업을 수행 해야하는 상황에 처한 것을 발견했습니다.이 작업을 수행하기 위해 mysql과 직접 작업했습니다. 내 코드는 post와 meta 테이블을 결합하여 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없이 모든 게시물과 모든 메타 값 (metas)을 가져옵니다.

게시물 ID 목록이 ID 배열로 저장된다고 가정 해 보겠습니다.

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

이제 적어도 약간의 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

솔루션 양식 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);

-1

다중 값 메타 필드 문제도 발생했습니다. 문제는 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로 설정됩니다. CHAR에 값을 CAST하는 것이 직렬화 된 문자열에 어떤 영향을 미치는지에 대한 세부 사항을 모르지만이를 수정하기 위해 SQL을 다음과 같이 캐스트를 제거 할 수 있습니다.

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

이제는 작동하지만 WordPress 내부를 사용하고 있기 때문에 다른 문제가 발생할 수 있으며 WordPress를 업그레이드해야한다고 가정하면 영구적 인 수정은 아닙니다.

내가 수정 한 방법은 원하는 메타 쿼리에 대해 WordPress에서 생성 한 SQL을 복사 한 다음 찾고있는 meta_values에 대한 추가 AND 문을 사용하여 PHP를 작성하고 $ wpdb-> get_results ($ sql ) 최종 출력. 해 키지 만 작동합니다.


나는 그것을 시도하지는 않았지만 get_meta_sql이 줄을 따르는 필터를 활용하는 것은 물론 핵심 코드를 해킹하는 것이 바람직합니다.
Steve Taylor
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.