[英]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數據庫文檔的鏈接。
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
現在我們有了絕對等效的查詢。
讓我們看看我們可以做的進一步。
IM.order_item_id = I.order_item_id
向myprefix_woocommerce_order_itemmeta
3個LEFT JOIN
看起來不必要的重復 meta_key
上有不同的謂詞 所以我們可以:
一種。 用一個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
編寫沒有數據的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.