如何提高查询执行时间?

问题描述 投票:-1回答:3

我有一个相当复杂的查询,执行需要1-2分钟。有没有办法改善执行时间?

这是查询:

select  o.orders_id, o.customers_id, o.customers_name, s.orders_status_name,
        ot.text as order_total, ot.value, DATEDIFF(NOW(), payment_data_read_status) as numDaysLeft,
        ( SELECT  ifnull(sum(op.paid_amount), 0)
            from  orders_payment op
            where  op.orders_id=o.orders_id
              AND  op.confirm_payment='1'
        ) as paid_total
    from  orders o, orders_total ot, orders_status s
    where  o.orders_id = ot.orders_id
      and  ot.class = 'ot_total'
      and  o.orders_status = s.orders_status_id
      and  s.language_id = '1'
      AND  ROUND(ot.value,2) != ROUND(
                ( SELECT  ifnull(sum(op.paid_amount),0)
                    from  orders_payment op
                    where  op.orders_id=o.orders_id
                      AND  op.confirm_payment='1'
                ), 2) 

查询说明

enter image description here

一些细节

订单中的记录数= 7321

orders_total = 22167中的记录数

orders_payment = 12038中的记录数

orders_status = 9中的记录数

orders_id列是订单表中的自动增量。首先我想在订单表中索引orders_id列,但因为它是主要的,所以我认为它不会起作用。

编辑错误

enter image description here

mysql sql query-optimization rdbms
3个回答
1
投票

我发现嵌套查询不一定是坏的,但我尽量避免将它们放在选择列表中。这是我的建议:

select
    o.orders_id,
    o.customers_id,
    o.customers_name,
    s.orders_status_name, 
    ot.text as order_total, 
    ot.value, 
    datediff(now(), payment_data_read_status) as numdaysleft, 
    ifnull(op.paid_total, 0) paid_total
from
    orders o
    join
    orders_total ot
    on o.orders_id = ot.orders_id 
    join
    orders_status s
    on o.orders_status = s.orders_status_id 
    left outer join
    (
        select 
            orders_id,
            sum(ifnull(paid_amount, 0)) as paid_total
        from
            orders_payment 
        where
            confirm_payment = '1'
        group by
            orders_id
    ) op 
    on
        op.orders_id = o.orders_id
where
    ot.class = 'ot_total' and 
    s.language_id = '1' and
    round(ot.value,2) != round(ifnull(op.paid_total, 0), 2);

我认为这会让优化者有更好的机会做好工作。

请注意,我在“op”的内部查询中添加了“group by”。如果没有这个,我想你可能会欺骗优化器为每个结果行而不是一次运行这个查询。

使用您拥有的卷,您不需要任何索引;他们可能会让事情变得更糟而不是更好,但要测试它,看看会发生什么。

我无法测试我的建议,但如果你提供创建表脚本和一些数据,我会这样做。如果我在查询中输入了任何拼写错误,请道歉。


0
投票

类似于Ron Ballard的回答,但是在子查询中进行舍入,并切换到显式连接语法: -

SELECT o.orders_id, 
        o.customers_id, 
        o.customers_name, 
        s.orders_status_name, 
        ot.text as order_total, 
        ot.value, 
        DATEDIFF(NOW(), payment_data_read_status) as numDaysLeft, 
        sub0.paid_amount_sum as paid_total 
FROM orders o
INNER JOIN orders_total ot ON o.orders_id = ot.orders_id
INNER JOIN orders_status s ON o.orders_status = s.orders_status_id 
INNER JOIN
(
    SELECT orders_id,
            COALESCE(SUM(op.paid_amount),0) AS paid_amount_sum,
            ROUND(COALESCE(SUM(op.paid_amount),0), 2) AS paid_amount_sum_rounded
    FROM orders_payment op 
    WHERE op.confirm_payment = '1'
    GROUP BY orders_id
) sub0
ON sub0.orders_id = o.orders_id 
WHERE ot.class = 'ot_total' 
AND s.language_id = '1' 
AND ROUND(ot.value,2) != sub0.paid_amount_sum_rounded

0
投票

可能233结果与55结果的问题是测试... != ...,其中一个可能是NULL。返回NULL,被视为假,因此好像=

解决这个问题的一种方法是添加

AND sub0.paid_amount_sum_rounded IS NOT NULL

所需索引:

o:  INDEX(orders_status, orders_id)  -- in this order
ot:  INDEX(class, orders_id)  -- in either order

从这个和其他答案的转移:

  • 使用JOIN ... ON ...语法。 (这是为了清楚起见;它对性能没有影响。)
  • 关注可能的LEFT JOIN结果,注意NULL和子查询。
  • 避免WHERE中的子查询。
  • 确保有合适的索引(以避免表扫描导致性能下降)。
  • 了解“复合”索引的好处。
© www.soinside.com 2019 - 2024. All rights reserved.