我有一个相当复杂的查询,执行需要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)
订单中的记录数= 7321
orders_total = 22167中的记录数
orders_payment = 12038中的记录数
orders_status = 9中的记录数
orders_id列是订单表中的自动增量。首先我想在订单表中索引orders_id列,但因为它是主要的,所以我认为它不会起作用。
编辑错误
我发现嵌套查询不一定是坏的,但我尽量避免将它们放在选择列表中。这是我的建议:
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”。如果没有这个,我想你可能会欺骗优化器为每个结果行而不是一次运行这个查询。
使用您拥有的卷,您不需要任何索引;他们可能会让事情变得更糟而不是更好,但要测试它,看看会发生什么。
我无法测试我的建议,但如果你提供创建表脚本和一些数据,我会这样做。如果我在查询中输入了任何拼写错误,请道歉。
类似于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
可能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
中的子查询。