sql >> データベース >  >> RDS >> Mysql

ワードプレスmeta_queryの最適化の助けが必要

    私はこの問題に遭遇し、問題はWordPressによって生成されたすべての内部結合にあることに気づきました。 WordPressから生のクエリを取得しました:

    SELECT   wp_posts.* FROM wp_posts  
    INNER JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id) 
    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)
    INNER JOIN wp_postmeta AS mt2 ON (wp_posts.ID = mt2.post_id)
    INNER JOIN wp_postmeta AS mt3 ON (wp_posts.ID = mt3.post_id)
    INNER JOIN wp_postmeta AS mt4 ON (wp_posts.ID = mt4.post_id)
    INNER JOIN wp_postmeta AS mt5 ON (wp_posts.ID = mt5.post_id) WHERE 1=1  AND ( wp_term_relationships.term_taxonomy_id IN (7,11,12,13,14,15) AND wp_posts.ID NOT IN (
                        SELECT object_id
                        FROM wp_term_relationships
                        WHERE term_taxonomy_id IN (10)
                    ) ) AND wp_posts.post_type IN ('post', 'page', 'attachment', 'product', 'store_page') AND (wp_posts.post_status = 'publish') AND ( (wp_postmeta.meta_key = 'product_type' AND CAST(wp_postmeta.meta_value AS CHAR) = 'type_pre_ground')
    OR  (mt1.meta_key = 'product_type2' AND CAST(mt1.meta_value AS CHAR) = 'type_pre_ground')
    OR  (mt2.meta_key = 'product_type3' AND CAST(mt2.meta_value AS CHAR) = 'type_pre_ground')
    OR  (mt3.meta_key = 'product_type4' AND CAST(mt3.meta_value AS CHAR) = 'type_pre_ground')
    OR  (mt4.meta_key = 'product_type5' AND CAST(mt4.meta_value AS CHAR) = 'type_pre_ground')
    OR  (mt5.meta_key = 'product_type6' AND CAST(mt5.meta_value AS CHAR) = 'type_pre_ground') ) GROUP BY wp_posts.ID ORDER BY wp_posts.post_date DESC
    

    これは決して完了せず、非常に高い負荷でCPUを拘束しました。最後の2つの内部結合(および対応するWHERE句)を削除し、2秒で結果を取得しました:

    SELECT   wp_posts.* FROM wp_posts  
    INNER JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id) 
    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)
    INNER JOIN wp_postmeta AS mt2 ON (wp_posts.ID = mt2.post_id)
    INNER JOIN wp_postmeta AS mt3 ON (wp_posts.ID = mt3.post_id) WHERE 1=1  AND ( wp_term_relationships.term_taxonomy_id IN (7,11,12,13,14,15) AND wp_posts.ID NOT IN (
                        SELECT object_id
                        FROM wp_term_relationships
                        WHERE term_taxonomy_id IN (10)
                    ) ) AND wp_posts.post_type IN ('post', 'page', 'attachment', 'product', 'store_page') AND (wp_posts.post_status = 'publish') AND ( (wp_postmeta.meta_key = 'product_type' AND CAST(wp_postmeta.meta_value AS CHAR) = 'type_pre_ground')
    OR  (mt1.meta_key = 'product_type2' AND CAST(mt1.meta_value AS CHAR) = 'type_pre_ground')
    OR  (mt2.meta_key = 'product_type3' AND CAST(mt2.meta_value AS CHAR) = 'type_pre_ground')
    OR  (mt3.meta_key = 'product_type4' AND CAST(mt3.meta_value AS CHAR) = 'type_pre_ground') ) GROUP BY wp_posts.ID ORDER BY wp_posts.post_date DESC 
    

    そのため、内部結合を削除することがクエリを高速化するための鍵であることに気付きました。 wp_postmeta INNER JOINを1つだけ使用してクエリを書き直し、数分の1秒で結果を取得しました:

    SELECT   wp_posts.* FROM wp_posts  
    INNER JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id) 
    INNER JOIN wp_postmeta ON (wp_posts.ID = wp_postmeta.post_id) WHERE 1=1  AND ( wp_term_relationships.term_taxonomy_id IN (7,11,12,13,14,15) AND wp_posts.ID NOT IN (
                        SELECT object_id
                        FROM wp_term_relationships
                        WHERE term_taxonomy_id IN (10)
                    ) ) 
    AND wp_posts.post_type IN ('post', 'page', 'attachment', 'product', 'store_page') 
    AND (wp_posts.post_status = 'publish') 
    AND ( (wp_postmeta.meta_key = 'product_type' AND CAST(wp_postmeta.meta_value AS CHAR) = 'type_pre_ground')
    OR  (wp_postmeta.meta_key = 'product_type2' AND CAST(wp_postmeta.meta_value AS CHAR) = 'type_pre_ground')
    OR  (wp_postmeta.meta_key = 'product_type3' AND CAST(wp_postmeta.meta_value AS CHAR) = 'type_pre_ground')
    OR  (wp_postmeta.meta_key = 'product_type4' AND CAST(wp_postmeta.meta_value AS CHAR) = 'type_pre_ground')
    OR  (wp_postmeta.meta_key = 'product_type5' AND CAST(wp_postmeta.meta_value AS CHAR) = 'type_pre_ground')
    OR  (wp_postmeta.meta_key = 'product_type6' AND CAST(wp_postmeta.meta_value AS CHAR) = 'type_pre_ground') ) 
    GROUP BY wp_posts.ID ORDER BY wp_posts.post_date DESC
    

    このソリューションを使用して、postmetaクエリをより高速な形式に変換するWordPressフィルターを作成しました:

    function custom_get_meta_sql( $meta_sql )
    {
        global $wpdb;
    
        $posts_table = $wpdb->prefix . 'posts';
        $postmeta_table = $wpdb->prefix . 'postmeta';
    
        //use single INNER JOIN
        $meta_sql['join'] = " INNER JOIN {$postmeta_table} AS pmta ON ({$posts_table}.ID = pmta.post_id) ";
    
        //replace the mtNN aliases with wp_postmeta
        $where_clause = $meta_sql['where'];
        $where_clause = str_replace("{$postmeta_table}.", 'pmta.', $where_clause);
        $where_clause = preg_replace('/mt\d+\.meta_/i', 'pmta.meta_', $where_clause);
    
        $meta_sql['where'] = $where_clause;
        return $meta_sql;
    }
    
    add_filter( 'get_meta_sql', 'custom_get_meta_sql' );
    

    このフィルターは、WordPressテーマのfunctions.phpファイルに配置する必要があります。



    1. A. *は、laravelクエリビルダーの左結合でGROUPBYに含まれていません

    2. SQLで絶対値を計算する方法

    3. MyBatisRowBoundsはクエリ結果を制限しません

    4. 列の最後の3文字で並べ替えるクエリ