如何改善查询执行时间?
How to improve query execution time?
我有一个相当复杂的查询,需要 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)
查询说明
一些细节
number of records in orders = 7321
number of records in orders_total = 22167
number of records in orders_payment= 12038
number of records in orders_status= 9
orders_id 列在订单中自动递增 table。首先,我想在订单 table 中索引 orders_id 列,但由于它是主要的,所以我认为它不会起作用。
编辑
错误
我发现嵌套查询不一定不好,但我尽量避免将它们放在 select 列表中。这是我的建议:
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"。如果没有它,我认为您可能会欺骗优化器 运行 此查询针对每个结果行而不是一次。
有了这些卷,您应该不需要任何索引;他们可能会使事情变得更糟而不是更好,但请测试一下,看看会发生什么。
我无法测试我的建议,但如果您提供创建 table 脚本和一些数据,我会这样做。如果我在查询中有任何拼写错误,我们深表歉意。
类似于 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
。 returns 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
. 中使用子查询
- 确保有 suitable 索引(以避免 table 扫描会降低性能)。
- 了解 'composite' 索引的好处。
我有一个相当复杂的查询,需要 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)
查询说明
一些细节
number of records in orders = 7321
number of records in orders_total = 22167
number of records in orders_payment= 12038
number of records in orders_status= 9
orders_id 列在订单中自动递增 table。首先,我想在订单 table 中索引 orders_id 列,但由于它是主要的,所以我认为它不会起作用。
编辑 错误
我发现嵌套查询不一定不好,但我尽量避免将它们放在 select 列表中。这是我的建议:
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"。如果没有它,我认为您可能会欺骗优化器 运行 此查询针对每个结果行而不是一次。
有了这些卷,您应该不需要任何索引;他们可能会使事情变得更糟而不是更好,但请测试一下,看看会发生什么。
我无法测试我的建议,但如果您提供创建 table 脚本和一些数据,我会这样做。如果我在查询中有任何拼写错误,我们深表歉意。
类似于 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
。 returns 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
. 中使用子查询
- 确保有 suitable 索引(以避免 table 扫描会降低性能)。
- 了解 'composite' 索引的好处。