簡體   English   中英

如何使此MySQL查詢(帶有子查詢)更快?

[英]How can I make this MySQL query (with a sub query) faster?

我有以下查詢,它獲取訂單數據, 可以正常工作,但需要13.63秒

SELECT
( SELECT meta_value FROM `myprefix_postmeta` as postmeta WHERE postmeta.meta_key = '_sku' AND postmeta.post_id = woocommerce_order_itemmeta3.meta_value ) AS sku,
woocommerce_order_items.order_item_name AS title,
SUM( woocommerce_order_itemmeta.meta_value ) AS quantity,
SUM( woocommerce_order_itemmeta2.meta_value ) AS total
FROM `myprefix_woocommerce_order_items` AS woocommerce_order_items
LEFT JOIN `myprefix_posts` AS posts ON posts.ID = woocommerce_order_items.order_id
LEFT JOIN `myprefix_woocommerce_order_itemmeta` AS woocommerce_order_itemmeta ON woocommerce_order_itemmeta.order_item_id = woocommerce_order_items.order_item_id
LEFT JOIN `myprefix_woocommerce_order_itemmeta` AS woocommerce_order_itemmeta2 ON woocommerce_order_itemmeta2.order_item_id =   woocommerce_order_items.order_item_id
LEFT JOIN `myprefix_woocommerce_order_itemmeta` AS woocommerce_order_itemmeta3 ON woocommerce_order_itemmeta3.order_item_id =   woocommerce_order_items.order_item_id
WHERE posts.post_type = 'shop_order'
AND posts.post_status IN( 'wc-completed', 'wc-processing', 'wc-on-hold' )
AND woocommerce_order_itemmeta.meta_key = '_qty'
AND woocommerce_order_itemmeta2.meta_key = '_line_total' 
AND woocommerce_order_itemmeta3.meta_key = '_product_id'
GROUP BY woocommerce_order_itemmeta3.meta_value
ORDER BY total DESC

我想優化此查詢以使其運行更快

也許只是花了很長時間才能返回。 但是,無論如何我都嘗試了刪除上面的子查詢,結果如下:

SELECT
postmeta.meta_value AS sku,
woocommerce_order_items.order_item_name AS title,
SUM( woocommerce_order_itemmeta.meta_value ) AS quantity,
SUM( woocommerce_order_itemmeta2.meta_value ) AS total
FROM `myprefix_woocommerce_order_items` AS woocommerce_order_items
LEFT JOIN `myprefix_posts` AS posts ON posts.ID = woocommerce_order_items.order_id
LEFT JOIN `myprefix_postmeta` AS postmeta ON posts.ID = woocommerce_order_items.order_id
LEFT JOIN `myprefix_woocommerce_order_itemmeta` AS woocommerce_order_itemmeta ON woocommerce_order_itemmeta.order_item_id = woocommerce_order_items.order_item_id
LEFT JOIN `myprefix_woocommerce_order_itemmeta` AS woocommerce_order_itemmeta2 ON woocommerce_order_itemmeta2.order_item_id =   woocommerce_order_items.order_item_id
LEFT JOIN `myprefix_woocommerce_order_itemmeta` AS woocommerce_order_itemmeta3 ON woocommerce_order_itemmeta3.order_item_id =   woocommerce_order_items.order_item_id
WHERE posts.post_type = 'shop_order'
AND posts.post_status IN( 'wc-completed', 'wc-processing', 'wc-on-hold' )
AND postmeta.meta_key = '_sku'
AND postmeta.post_id = woocommerce_order_itemmeta3.meta_value
AND woocommerce_order_itemmeta.meta_key = '_qty'
AND woocommerce_order_itemmeta2.meta_key = '_line_total' 
AND woocommerce_order_itemmeta3.meta_key = '_product_id'
GROUP BY woocommerce_order_itemmeta3.meta_value
ORDER BY total DESC

這幾毫秒快一點 ,但是有什么方法可以進一步優化嗎?

非常感謝您可能需要了解這些表背后的數據,它們是標准的WordPress數據庫表WooCommerce數據庫架構 ,我無法真正共享這些表背后的數據。

上面的第二個示例存在一個附帶問題,即如果SKU為NULL,則不包含該行,而最上面的一個包含該行,因此當前仍然是首選查詢。

更新#1

根據答案: https : //stackoverflow.com/a/54202562/8369600

我最初從答案的第1部分中更新了我的代碼格式,並且效果很好。

然后,我在步驟2中使用了代碼,並對格式進行了一些小的更改,以便在phpMyAdmin中為我工作,這些更改是:

SELECT
    (SELECT meta_value
        FROM `myprefix_postmeta` as postmeta 
        WHERE 
            postmeta.meta_key = '_sku' 
            AND postmeta.post_id = ( -- Substitute meta_value with CASE-expressions 
                    CASE
                        WHEN IM.meta_key = '_product_id' THEN IM.meta_value
                        ELSE NULL
                    END 
                ) -- IM3.meta_value               
            ) AS sku,
            -- think above is okay ^^^^
    I.order_item_name AS title,
    SUM(
        CASE -- Substitute meta_value with CASE-expressions 
            WHEN IM.meta_key = '_qty' THEN IM.meta_value
            ELSE 0 
        END 
    ) AS quantity,
    -- IM2.meta_value ) AS total
SUM( -- Substitute meta_value with CASE-expressions 
    CASE
            WHEN IM.meta_key = '_line_total' THEN IM.meta_value
            ELSE 0
    END 
) AS total       

FROM 
    `myprefix_woocommerce_order_items` AS I
    LEFT JOIN `myprefix_posts` AS posts 
    ON posts.ID = I.order_id
    LEFT JOIN `myprefix_woocommerce_order_itemmeta` AS IM -- Make readable aliases
        ON IM.order_item_id = I.order_item_id 
            AND IM.meta_key IN('_qty', '_line_total', '_product_id')
    --         AND IM.meta_key = '_qty'   -- Move predicates from WHERE
    -- LEFT JOIN `myprefix_woocommerce_order_itemmeta` AS IM2 
    --     ON IM2.order_item_id =   I.order_item_id
    --         AND IM2.meta_key = '_line_total' -- Move predicates from WHERE
    -- LEFT JOIN `myprefix_woocommerce_order_itemmeta` AS IM3 
    --     ON IM3.order_item_id =   I.order_item_id
    --         AND IM3.meta_key = '_product_id' -- Move predicates from WHERE
WHERE 
    posts.post_type = 'shop_order'
    AND posts.post_status IN
    ( 'wc-completed', 'wc-processing', 'wc-on-hold' )
GROUP BY 
    (
        CASE
            WHEN IM.meta_key = '_product_id' THEN IM.meta_value
            ELSE NULL
        END 
    ) -- IM3.meta_value    
ORDER BY 
    total DESC

但是,這返回:

在此處輸入圖片說明

在所有行都填充有數據之前,請懷疑案件是否沒有按照原始規定進行處理。

注意我還按照前面的注釋之一的要求添加了指向WooCommerce數據庫文檔的鏈接。

步驟1:創建可讀的別名並將meta_key -predicates移到JOIN

為了清楚起見,我采取了自由格式化您的查詢。

我重命名了別名,並將meta_key -predicates從WHERE移到JOIN

SELECT
    (SELECT meta_value
        FROM `myprefix_postmeta` as postmeta 
        WHERE 
            postmeta.meta_key = '_sku' 
            AND postmeta.post_id = IM3.meta_value ) AS sku,
    I.order_item_name AS title,
    SUM (IM.meta_value ) AS quantity,
    SUM (IM2.meta_value ) AS total
FROM 
    `myprefix_woocommerce_order_items` AS I
    LEFT JOIN `myprefix_posts` AS posts 
    ON posts.ID = I.order_id
    LEFT JOIN `myprefix_woocommerce_order_itemmeta` AS IM -- Make readable aliases
        ON IM.order_item_id = I.order_item_id 
            AND IM.meta_key = '_qty'   -- Move predicates from WHERE
    LEFT JOIN `myprefix_woocommerce_order_itemmeta` AS IM2 
        ON IM2.order_item_id =   I.order_item_id
            AND IM2.meta_key = '_line_total' -- Move predicates from WHERE
    LEFT JOIN `myprefix_woocommerce_order_itemmeta` AS IM3 
        ON IM3.order_item_id =   I.order_item_id
            AND IM3.meta_key = '_product_id' -- Move predicates from WHERE
WHERE 
    posts.post_type = 'shop_order'
    AND posts.post_status IN
    ( 'wc-completed', 'wc-processing', 'wc-on-hold' )
GROUP BY 
    IM3.meta_value
ORDER BY 
    total DESC

現在我們有了絕對等效的查詢。

讓我們看看我們可以做的進一步。

  1. 使用相同謂詞IM.order_item_id = I.order_item_idmyprefix_woocommerce_order_itemmeta 3個LEFT JOIN看起來不必要的重復
  2. 但是我們在meta_key上有不同的謂詞

步驟2:僅用1個替換3個JOIN,用CASE表達式替換JOINed字段

所以我們可以:

一種。 用一個JOIN替換3個JOIN

用CASE表達式替換IM / IM2 / IM3字段

SELECT
    (SELECT meta_value
        FROM `myprefix_postmeta` as postmeta 
        WHERE 
            postmeta.meta_key = '_sku' 
            AND postmeta.post_id = ( -- Substitute meta_value with CASE-expressions 
                    CASE
                        WHEN IM.meta_key = '_product_id' THEN IM.meta_value
                        ELSE NULL
                    END 
                ) -- IM3.meta_value               
            ) AS sku,
    I.order_item_name AS title,
    SUM (
        CASE -- Substitute meta_value with CASE-expressions 
            WHEN IM.meta_key = '_qty' THEN IM.meta_value
            ELSE 0 
        END 
    ) AS quantity
    -- IM2.meta_value ) AS total
SUM ( -- Substitute meta_value with CASE-expressions 
    CASE
            WHEN IM.meta_key = '_line_total' THEN IM.meta_value
            ELSE 0
    END 
) AS total       

FROM 
    `myprefix_woocommerce_order_items` AS I
    LEFT JOIN `myprefix_posts` AS posts 
    ON posts.ID = I.order_id
    LEFT JOIN `myprefix_woocommerce_order_itemmeta` AS IM -- Make readable aliases
        ON IM.order_item_id = I.order_item_id 
            AND IM.meta_key IN ('_qty', '_line_total', '_product_id')
    --         AND IM.meta_key = '_qty'   -- Move predicates from WHERE
    -- LEFT JOIN `myprefix_woocommerce_order_itemmeta` AS IM2 
    --     ON IM2.order_item_id =   I.order_item_id
    --         AND IM2.meta_key = '_line_total' -- Move predicates from WHERE
    -- LEFT JOIN `myprefix_woocommerce_order_itemmeta` AS IM3 
    --     ON IM3.order_item_id =   I.order_item_id
    --         AND IM3.meta_key = '_product_id' -- Move predicates from WHERE
WHERE 
    posts.post_type = 'shop_order'
    AND posts.post_status IN
    ( 'wc-completed', 'wc-processing', 'wc-on-hold' )
GROUP BY 
    (
        CASE
            WHEN IM.meta_key = '_product_id' THEN IM.meta_value
            ELSE NULL
        END 
    ) -- IM3.meta_value    
ORDER BY 
    total DESC

PS

編寫沒有數據的SQL並不是很舒服。

而且此查詢可能不適用於每個版本的mysql。

因此,請嘗試理解原理,而不只是復制粘貼我的代碼。

通知我們有關結果或問題。

如果woocommerce是WordPress的分支,那么他們可能在索引myprefix_woocommerce_order_itemmeta 調整此處的提示以提高性能。

我同意別人的看法-EAV模式模式糟透了。 上述幫助我的一些指標的建議。

例如,“ order”始終具有數量,product_id,line_total; 那么,為什么不將它們作為真實的列而不是將它們隱藏在另一個表中,就好像它們是稀有屬性一樣呢? 對於只有一些商品具有“衣着尺寸”或“ F-stop”或“傳輸類型”的購物網站,EAV有點必要。 (在這種情況下,JSON 可能比EAV更好。)

模式審查

不錯,woocommerce比WP更糟糕( 參考 ):

CREATE TABLE {$wpdb->prefix}woocommerce_order_itemmeta (
  meta_id BIGINT UNSIGNED NOT NULL auto_increment,
  order_item_id BIGINT UNSIGNED NOT NULL,
  meta_key varchar(255) default NULL,
  meta_value longtext NULL,
  PRIMARY KEY  (meta_id),
  KEY order_item_id (order_item_id),
  KEY meta_key (meta_key(32))
) $collate;

前綴索引( meta_key(32) )很少有用,並且通常適得其反。 盡管如此,可能我所有的建議(請參見上面的鏈接)都適用,並且名稱進行了適當的更改。

首先要做的是檢查在LEFT JOIN和過濾位置的所有列上是否都有正確的索引。 通常,缺少索引是導致查詢緩慢的原因。

如果您想走得更遠,我將重建索引。 也許一些零碎的東西也放慢了速度。

盡管不是必需的,您也可以嘗試將wc-completed,wc-processing之類的遷移鍵放入自己的表中,並通過INT進行聯接。

您也可以嘗試規范化SUM甚至為它們創建一個計算列。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM