[英]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.