Đặt bí danh cho các đối số meta_query trong get_posts ()


8

Có cách nào để đặt bí danh cho các đối số meta_query khi chạy a get_posts()không? Một trong những truy vấn của tôi là hoạt động kém. Để tối ưu hóa tôi chỉ cần có thể sử dụng lại cùng một bảng đã tham gia thay vì tham gia vào 3 bảng khi chỉ cần một bảng.

Ví dụ hiện tại của tôi ...

$args = array(
    'meta_query' => array(
        'relation' => 'AND',
        array(
            'key' => 'abc_type',
            'value' => array('puppy', 'kitten'),
            'compare' => 'IN',
        ),
        array(
            'relation' => 'OR',
            array(
                'relation' => 'AND',
                array(
                    'key' => 'abc_type',
                    'value' => 'puppy',
                    'compare' => '=',
                ),
                array(
                    'key' => 'abc_color',
                    'value' => 'pink',
                    'compare' => '=',
                ),
            ),
            array(
                'relation' => 'AND',
                array(
                    'key' => 'abc_type',
                    'value' => 'kitten',
                    'compare' => '=',
                ),
                array(
                    'key' => 'abc_size',
                    'value' => 'large',
                    'compare' => '=',
                ),
            ),
        ),
    )
);
get_posts($args);

về cơ bản chuyển thành điều này trong SQL thẳng ...

SELECT posts.* FROM posts
INNER JOIN postmeta ON ( posts.ID = postmeta.post_id )
INNER JOIN postmeta AS mt1 ON ( posts.ID = mt1.post_id )
INNER JOIN postmeta AS mt2 ON ( posts.ID = mt2.post_id )
INNER JOIN postmeta AS mt3 ON ( posts.ID = mt3.post_id )
WHERE 1=1
AND
( 
  ( postmeta.meta_key = 'abc_type' AND postmeta.meta_value IN ('puppy','kitten') ) 
  AND 
  ( 
    ( 
      ( mt1.meta_key = 'abc_type' AND mt1.meta_value = 'puppy' ) 
      AND 
      ( mt2.meta_key = 'abc_color' AND mt2.meta_value > 'pink' )
    ) 
    OR 
    ( 
      ( mt3.meta_key = 'abc_type' AND mt3.meta_value = 'kitten' )
      AND
      ( mt4.meta_key = 'abc_size' AND mt4.meta_value = 'large' )
    )
  )
) AND posts.post_type = 'abc_mypost' AND ((posts.post_status = 'publish'))
GROUP BY posts.ID ORDER BY posts.post_title ASC;

Tuy nhiên, điều này đang bổ sung thêm 2 phép nối bổ sung cho trường meta tùy chỉnh abc_typevà vì hiệu suất như vậy đã tạo ra một cú hích lớn. Có cách nào để có thể tham chiếu cùng một bí danh cho nhiều đối số meta_query không? Về cơ bản, mt1mt3hoàn toàn không cần thiết, tôi chỉ có thể tham khảo postmetabảng đầu tiên được sử dụng với bảng đầu tiên ( postmeta.meta_key = 'abc_type' AND postmeta.meta_value IN ('puppy','kitten') ). Hoặc ít nhất nếu tôi có thể đặt bí danh tùy chỉnh trên mỗi trong số này, tôi có thể tham khảo điều đó.

Một truy vấn tối ưu hơn sẽ là ...

SELECT posts.* FROM posts
INNER JOIN postmeta ON ( posts.ID = postmeta.post_id )
INNER JOIN postmeta AS mt1 ON ( posts.ID = mt1.post_id )
INNER JOIN postmeta AS mt2 ON ( posts.ID = mt2.post_id )
WHERE 1=1
AND
( 
  ( postmeta.meta_key = 'abc_type' AND postmeta.meta_value IN ('puppy','kitten') ) 
  AND 
  ( 
    ( 
      ( postmeta.meta_key = 'abc_type' AND postmeta.meta_value = 'puppy' ) 
      AND 
      ( mt1.meta_key = 'abc_color' AND mt1.meta_value > 'pink' )
    ) 
    OR 
    ( 
      ( postmeta.meta_key = 'abc_type' AND postmeta.meta_value = 'kitten' )
      AND
      ( mt2.meta_key = 'abc_color' AND mt2.meta_value = 'green' )
    )
  )
) AND posts.post_type = 'abc_mypost' AND ((posts.post_status = 'publish'))
GROUP BY posts.ID ORDER BY posts.post_title ASC;

Suy nghĩ?


Bạn đã tìm thấy một giải pháp, hay đây vẫn là một vấn đề mở?
fuxia

Đây vẫn là một vấn đề mở. Tuy nhiên, tôi không chắc là nó có thể. Tôi cuối cùng đã phải sử dụng một truy vấn MySQL thẳng thay vì đi qua get_posts().
Bàn chân cọ xát

1
Tôi nghĩ đó là một câu hỏi rất thú vị. Vì vậy, tôi chỉ thêm gia vị một chút. :)
fuxia

Bộ posts_wherelọc có thể hữu ích.
Nathan Johnson

Câu trả lời:


4

Có một cái nhìn vào meta_query_find_compatible_table_aliasbộ lọc được xác định trong wp-includes/class-wp-meta-query.php. Tài liệu của bộ lọc này:

/**
 * Filters the table alias identified as compatible with the current clause.
 *
 * @since 4.1.0
 *
 * @param string|bool $alias        Table alias, or false if none was found.
 * @param array       $clause       First-order query clause.
 * @param array       $parent_query Parent of $clause.
 * @param object      $this         WP_Meta_Query object.
 */
return apply_filters( 'meta_query_find_compatible_table_alias', $alias, $clause, $parent_query, $this );

Có khả năng là hàm gọi find_compatible_table_alias, đang trả về false và do đó truy vấn tạo ra các mt*bí danh. Dưới đây là một số mã mẫu sử dụng bộ lọc này, mặc dù cá nhân tôi sẽ ủng hộ cho một cái gì đó dễ hiểu hơn một chút. Sửa đổi các truy vấn như thế này có thể dẫn đến vô số vấn đề đau đầu và có thể không rõ ràng ở nơi mà truy vấn đang bị rối tung, đặc biệt là nếu bạn đưa vào các nhà phát triển khác trong tương lai. Mà nói...

// Reuse the same alias for the abc_type meta key.
function pets_modify_meta_query( $alias, $meta_query ) {
    if ( 'abc_type' === $meta_query['key'] ) {
        return 'mt1';
    }

    return $alias;
}

// Filter the query.
add_filter( 'meta_query_find_compatible_table_alias', 'pets_modify_meta_query', 10, 2 );

$args = array(
    'meta_query' => array(
        'relation' => 'AND',
        array(
            'key' => 'abc_type',
            'value' => array('puppy', 'kitten'),
            'compare' => 'IN',
        ),
        array(
            'relation' => 'OR',
            array(
                'relation' => 'AND',
                array(
                    'key' => 'abc_type',
                    'value' => 'puppy',
                    'compare' => '=',
                ),
                array(
                    'key' => 'abc_color',
                    'value' => 'pink',
                    'compare' => '=',
                ),
            ),
            array(
                'relation' => 'AND',
                array(
                    'key' => 'abc_type',
                    'value' => 'kitten',
                    'compare' => '=',
                ),
                array(
                    'key' => 'abc_size',
                    'value' => 'large',
                    'compare' => '=',
                ),
            ),
        ),
    )
);

$q = new WP_Query($args);
echo '<pre>', print_r($q->request, true); die;

Điều này dẫn đến một truy vấn như

SELECT SQL_CALC_FOUND_ROWS
    wp_posts.ID
FROM wp_posts
INNER JOIN wp_postmeta ON ( wp_posts.ID = wp_postmeta.post_id )
INNER JOIN wp_postmeta AS mt1 ON ( wp_posts.ID = mt1.post_id )
WHERE
    1=1
AND
(
    ( mt1.meta_key = 'abc_type' AND mt1.meta_value IN ('puppy','kitten') )
    AND
    (
        (
            ( mt1.meta_key = 'abc_type' AND mt1.meta_value = 'puppy' )
            AND
            ( wp_postmeta.meta_key = 'abc_color' AND wp_postmeta.meta_value = 'pink' )
        )
        OR
        (
            ( mt1.meta_key = 'abc_type' AND mt1.meta_value = 'kitten' )
            AND
            ( mt1.meta_key = 'abc_size' AND mt1.meta_value = 'large' )
        )
    )
)
AND
    wp_posts.post_type = 'post'
AND (
    wp_posts.post_status = 'publish'
    OR
    wp_posts.post_status = 'future'
    OR
    wp_posts.post_status = 'draft'
    OR wp_posts.post_status = 'pending'
)
GROUP BY wp_posts.ID ORDER BY wp_posts.post_date DESC LIMIT 0, 10

3

Bạn có thể sử dụng posts_whereposts_joinbộ lọc để sửa đổi truy vấn. Nó không phải là rất thanh lịch, nhưng bạn sẽ có thể gây rối với hai bộ lọc này để sql của bạn được tối ưu hóa hơn. Đó là loại vũ phu, nhưng tôi không thể thấy một cách tốt hơn trong lớp WP_Query. Điều đó không nói rằng mặc dù không có.

//* Make sure to not suppress filters
$args = array(
  'suppress_filters' => false,
  //* rest of args unchanged
);

add_filter( 'posts_where', function( $sql ) {
  $sql = str_replace(
    "( mt1.meta_key = 'abc_type' AND mt1.meta_value = 'puppy' )",
    "( postmeta.meta_key = 'abc_type' AND postmeta.meta_value = 'puppy' )",
    $sql
  );

  $sql = str_replace(
    "( mt3.meta_key = 'abc_type' AND mt3.meta_value = 'kitten' )",
    "( postmeta.meta_key = 'abc_type' AND postmeta.meta_value = 'kitten' )",
    $sql
  );

  $sql = str_replace( [ 'mt2', 'mt4' ], [ 'mt1', 'mt2' ], $sql );
  return $sql;
});

add_filter( 'posts_join', function( $sql ) {
  $sql = str_replace(
    " INNER JOIN wp_postmeta AS mt4 ON ( wp_posts.ID = mt4.post_id )",
    "",
    $sql
  );
  $sql = str_replace(
    " INNER JOIN wp_postmeta AS mt3 ON ( wp_posts.ID = mt3.post_id )",
    "",
    $sql
  );
  return $sql;
});

Có lẽ nên có một số kiểm tra trong đó để bạn không vô tình sửa đổi các truy vấn khác. Đó là một bài tập cho người đọc.


0

Bạn có thể tối ưu hóa truy vấn của mình bằng cách xóa truy vấn meta đầu tiên vì nó là dự phòng, như vậy:

$args = array(
    'meta_query' => array(
        'relation' => 'OR',
        array(
            'relation' => 'AND',
            array(
                'key' => 'abc_type',
                'value' => 'puppy',
                'compare' => '=',
            ),
            array(
                'key' => 'abc_color',
                'value' => 'pink',
                'compare' => '=',
            ),
        ),
        array(
            'relation' => 'AND',
            array(
                'key' => 'abc_type',
                'value' => 'kitten',
                'compare' => '=',
            ),
            array(
                'key' => 'abc_size',
                'value' => 'large',
                'compare' => '=',
            ),
        ),
    ),
);
get_posts($args);

Bằng cách này, bạn sẽ chỉ nhận được pink puppyhoặc large kitten, như bạn dự định, tôi tin.

Đối với việc tối ưu hóa các truy vấn MySQL nội bộ của WordPress, tôi tin rằng bạn nên tránh điều đó vì bạn sẽ phơi bày ra các tác dụng phụ có thể xảy ra. Bạn sẽ tốt hơn nếu dựa vào thực tế là các truy vấn được lưu trữ và thực hiện thêm một số xử lý PHP trên tập dữ liệu (lớn hơn). Tôi tin rằng điều này sẽ dẫn đến hiệu suất tổng thể tốt hơn vì nút cổ chai không phải là lượng dữ liệu bạn trích xuất từ ​​cơ sở dữ liệu, mà là khó khăn trong việc thu thập dữ liệu (có bao nhiêu truy vấn). PHP khá nhanh khi đi qua các mảng.

Vì vậy, tôi tin rằng một tình huống như thế này nhanh hơn, khi xem xét rằng meta bài đăng được lưu trữ:

$args = array(
    'meta_query' => array( 
        array(
            'key' => 'abc_type',
            'value' => array('puppy', 'kitten'),
            'compare' => 'IN',
        ),
    ),
);

$final_posts = array();
foreach( $get_posts($args) as $post ) {
    if ( 'puppy' === get_post_meta( $post->ID, 'abc_type', true ) ) {
        if ( 'pink' === get_post_meta( $post->ID, 'abc_color', true ) ) {
            $final_posts[] = $post;
        }
    } else {
        // This is definitely a kitten
        if ( 'large' === get_post_meta( $post->ID, 'abc_size', true ) ) {
            $final_posts[] = $post;
        }
    }
}

-2

Tôi không thực sự là một anh chàng cơ sở dữ liệu, nhưng tôi đã chơi một lần trên TV một lần ...

Sẽ không phần này

SELECT posts.* FROM posts
INNER JOIN postmeta ON ( posts.ID = postmeta.post_id )
INNER JOIN postmeta AS mt1 ON ( posts.ID = mt1.post_id )
INNER JOIN postmeta AS mt2 ON ( posts.ID = mt2.post_id )

được thay thế tốt hơn với

SELECT posts.* FROM posts
INNER JOIN postmeta ON (( posts.ID = postmeta.post_id ) and 
( posts.ID = mt1.post_id ) and
( posts.ID = mt2.post_id ))

Điều đó có thể có thể được đơn giản hóa hơn nữa..với một số bí danh bị mắc kẹt ở đó ở vị trí thích hợp để bạn có thể sử dụng phần còn lại của truy vấn của mình.

Chỉ là một suy nghĩ ...


1
Vấn đề của anh ấy là anh ấy đang sử dụng get_posts(), vì vậy anh ấy không tự viết truy vấn.
Jacob Peattie

okie dokie. Đó là lý do tại sao "đó chỉ là một ý nghĩ" và tại sao tôi không phải là một anh chàng cơ sở dữ liệu.
Rick Hellewell

@RickHellewell Tôi không chắc chắn 100% cách trả lời câu hỏi này hoặc những gì nó làm, mặc dù nó rất thú vị. Có lẽ để lại các ghi chú hữu ích như các bình luận liên kết với các ý chính để tránh các downvote?
Tom J Nowell

Chà, @TomJNowell, câu trả lời đó là 2 năm rưỡi trước .... và có lẽ tôi đã học được thêm một chút kể từ đó, và đôi khi (hy vọng) tốt hơn trong việc cung cấp câu trả lời ....
Rick Hellewell
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.