使用 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_categoryoc_order_product 是多对多映射表,请在此处查看优化索引的规则:http://mysql.rjweb.org/doc.php/index_cookbook_mysql#many_to_many_mapping_table

我看不出这里需要分组:

GROUP_CONCAT(cd.name SEPARATOR ' / ')

进行这些更改后,如果您想进一步讨论,请为每个表格提供SHOW CREATE TABLEEXPLAIN SELECT ... 用于查询。

每个表有多少行?结果有多少没有最后的LIMIT 12

【讨论】:

【参考方案2】:

使用CTE 可能会有一些好处。如果不是在性能方面,至少在可读性方面。但是为了更好地了解瓶颈所在,您应该获取执行计划EXPLAIN SELECT。您可能还需要额外的索引,但如果不知道表结构和数据量就很难判断。

为什么要引用显然是整数的字段?这可能是您的问题的一部分,here is why。日期值应该被引用,这是可以的,但没有数字。

【讨论】:

以上是关于使用 CASE WHEN 进行查询更快的主要内容,如果未能解决你的问题,请参考以下文章

无法使用 getContentResolver().query() 进行 CASE WHEN 查询

在 WHERE 中使用“CASE WHEN”语句进行查询会导致 QuerySyntaxException:意外 AST

select case when,用来判断查询

SQL查询语句SELECT中带有case when嵌套子查询判断的问题

case when用法sql

查询多个结果是用case when 条件显示列的查询效率高,还是查询后union all合并效率高.