使用 CASE WHEN 进行查询更快
Posted
技术标签:
【中文标题】使用 CASE WHEN 进行查询更快【英文标题】:make queries using CASE WHEN faster 【发布时间】:2021-03-14 08:06:58 【问题描述】:我有这个问题
SELECT t1.date_added, t1.order_id, t1.firstname AS customer, t1.name AS product_name, t1.category, t1.supplier, t1.quantity, t1.price, t1.total
FROM (
SELECT o.date_added, op.order_id, o.firstname, op.name, op.price, op.total, op.quantity, m.name AS supplier,
(CASE WHEN skps.buy_price IS NULL THEN (SELECT skps2.buy_price FROM `oc_stock_kps` skps2 WHERE skps2.buy_price != '0' ORDER BY skps2.id DESC LIMIT 1)
ELSE skps.buy_price
END) AS buy_price,
(SELECT GROUP_CONCAT(cd.name SEPARATOR ' / ') FROM oc_category_description cd LEFT JOIN product_to_category ptc ON (ptc.category_id = cd.category_id) WHERE ptc.product_id = p.product_id) AS category,
LAG(op.name) OVER(ORDER BY op.order_product_id) prev
FROM `oc_order_product` op
LEFT JOIN oc_order o ON (op.order_id = o.order_id)
LEFT JOIN oc_product p ON (op.product_id = p.product_id)
LEFT JOIN oc_manufacturer m ON (p.manufacturer_id = m.manufacturer_id)
LEFT JOIN oc_stock_kps skps ON (skps.product_id = op.product_id AND skps.order_id = op.order_id)
WHERE (o.date_added BETWEEN '2021-02-01 00:00:00' AND '2021-02-28 23:59:00')
AND p.product_id != '0'
AND o.order_status_id = '5'
) t1
WHERE t1.prev IS NULL OR t1.name<>t1.prev
ORDER BY t1.date_added
LIMIT 12
问题是我每次使用这个
(CASE WHEN skps.buy_price IS NULL
THEN (SELECT skps2.buy_price FROM `oc_stock_kps` skps2
WHERE skps2.buy_price != '0'
ORDER BY skps2.id DESC LIMIT 1)
ELSE skps.buy_price
END) AS buy_price,
还有这个
LEFT JOIN oc_stock_kps skps
ON (skps.product_id = op.product_id
AND skps.order_id = op.order_id)
查询时间过长。 有什么办法让它更快?
【问题讨论】:
在连接条件中包含AND
会损害性能。您可以尝试添加适当的索引,或者重新考虑您的设计以避免需要复杂的连接。
如果是我,我会扔掉它,从头开始
这不会很好地扩展。必须先计算 t1
的所有行,然后才能选择最旧的 12 行。但是,您已经知道这些将是最早的 12 个订单,因此您可以将您的问题限制为仅使用这些订单。换句话说,将排序和限制移动到t1
内部,而不是在它之外。
【参考方案1】:
迟缓可能出现在CASE
的子查询中,而不是CASE
本身。
要添加的复合和覆盖索引。将列按给定的顺序排列:
o: (order_status_id, date_added, order_id, firstname)
skps: (order_id, product_id, buy_price)
p: (product_id, manufacturer_id)
如果product_to_category
和oc_order_product
是多对多映射表,请在此处查看优化索引的规则:http://mysql.rjweb.org/doc.php/index_cookbook_mysql#many_to_many_mapping_table
我看不出这里需要分组:
GROUP_CONCAT(cd.name SEPARATOR ' / ')
进行这些更改后,如果您想进一步讨论,请为每个表格提供SHOW CREATE TABLE
。 EXPLAIN SELECT ...
用于查询。
每个表有多少行?结果有多少没有最后的LIMIT 12
?
【讨论】:
【参考方案2】:使用CTE 可能会有一些好处。如果不是在性能方面,至少在可读性方面。但是为了更好地了解瓶颈所在,您应该获取执行计划EXPLAIN SELECT
。您可能还需要额外的索引,但如果不知道表结构和数据量就很难判断。
为什么要引用显然是整数的字段?这可能是您的问题的一部分,here is why。日期值应该被引用,这是可以的,但没有数字。
【讨论】:
以上是关于使用 CASE WHEN 进行查询更快的主要内容,如果未能解决你的问题,请参考以下文章
无法使用 getContentResolver().query() 进行 CASE WHEN 查询
在 WHERE 中使用“CASE WHEN”语句进行查询会导致 QuerySyntaxException:意外 AST