为什么向我的查询添加 WHERE 语句(在具有索引的列上)会使我的 运行 时间从几秒增加到几分钟?
Why does adding a WHERE statement (on a column with an index) to my query increase my run time from seconds to minutes?
我的问题是 MySQL 中的这个查询:
select
SUM(OrderThreshold < @LOW_COST) as LOW_COUNT,
SUM(OrderThreshold > @HIGH_COST) as HIGH_COUNT
FROM parts
-- where parttypeid = 1
当 where
取消注释时,我的 运行 时间跳了 4.5 秒到 341 秒。 table 中总共有大约 2100 万条记录。
我的 EXPLAIN
看起来像这样,这似乎表明它正在使用我在 PartTypeId
.
上的索引
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE parts ref PartTypeId PartTypeId 1 const 11090057
我使用此查询创建了 table:
CREATE TABLE IF NOT EXISTS parts (
Id INTEGER NOT NULL PRIMARY KEY,
PartTypeId TINYINT NOT NULL,
OrderThreshold INTEGER NOT NULL,
PartName VARCHAR(500),
INDEX(Id),
INDEX(PartTypeId),
INDEX(OrderThreshold),
);
没有 WHERE
returns
的查询
LOW_COUNT HIGH_COUNT
3570 3584
使用 where
结果如下所示:
LOW_COUNT HIGH_COUNT
2791 2147
添加仅查看一列的 where
语句时,如何提高查询性能以将 运行 时间减少到秒(而不是分钟)范围内?
尝试
select SUM(OrderThreshold < @LOW_COST) as LOW_COUNT,
SUM(OrderThreshold > @HIGH_COST) as HIGH_COUNT
from parts
where parttypeid = 1
and OrderThreshold not between @LOW_COST and @HIGH_COST
和
select count(*) as LOW_COUNT, null as HIGH_COUNT
from parts
where parttypeid = 1
and OrderThreshold < @LOW_COST
union all
select null, count(*)
from parts
where parttypeid = 1
and OrderThreshold > @HIGH_COST
您接受的答案没有解释您的原始查询出了什么问题:
select SUM(OrderThreshold < @LOW_COST) as LOW_COUNT,
SUM(OrderThreshold > @HIGH_COST) as HIGH_COUNT
from parts
where parttypeid = 1;
正在使用索引查找结果,但有很多行 parttypeid = 1
。我猜测每个数据页可能至少有一个这样的行。这意味着正在获取所有行,但它们被乱序读取。这比只进行完整的 table 扫描(如在第一个查询中)要慢。换句话说,正在读取所有数据页,但索引增加了额外的开销。
正如 Juergen 指出的那样,更好的查询形式将条件移动到 where
子句中:
select SUM(OrderThreshold < @LOW_COST) as LOW_COUNT,
SUM(OrderThreshold > @HIGH_COST) as HIGH_COUNT
from parts
where parttypeid = 1 AND
(OrderThreshold < @LOW_COST OR OrderThreshold > @HIGH_COST)
(我更喜欢这种形式,因为 where
条件与 case
条件匹配。)对于此查询,您需要 parts(parttypeid, OrderThreshold)
上的索引。在这种情况下,我不确定 MySQL 优化器,但最好写成:
select 'Low' as which, count(*) as CNT
from parts
where parttypeid = 1 AND
OrderThreshold < @LOW_COST
union all
select 'High', count(*) as CNT
from parts
where parttypeid = 1 AND
OrderThreshold > @HIGH_COST;
在这种情况下,每个子查询肯定应该使用索引。 (如果你想让它们排成两列,有几种方法可以实现,但我猜这不是那么重要。)
不幸的是,没有 where
子句的查询的最佳索引是 parts(OrderThreshold)
。这与上面的索引不同。
我的问题是 MySQL 中的这个查询:
select
SUM(OrderThreshold < @LOW_COST) as LOW_COUNT,
SUM(OrderThreshold > @HIGH_COST) as HIGH_COUNT
FROM parts
-- where parttypeid = 1
当 where
取消注释时,我的 运行 时间跳了 4.5 秒到 341 秒。 table 中总共有大约 2100 万条记录。
我的 EXPLAIN
看起来像这样,这似乎表明它正在使用我在 PartTypeId
.
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE parts ref PartTypeId PartTypeId 1 const 11090057
我使用此查询创建了 table:
CREATE TABLE IF NOT EXISTS parts (
Id INTEGER NOT NULL PRIMARY KEY,
PartTypeId TINYINT NOT NULL,
OrderThreshold INTEGER NOT NULL,
PartName VARCHAR(500),
INDEX(Id),
INDEX(PartTypeId),
INDEX(OrderThreshold),
);
没有 WHERE
returns
LOW_COUNT HIGH_COUNT
3570 3584
使用 where
结果如下所示:
LOW_COUNT HIGH_COUNT
2791 2147
添加仅查看一列的 where
语句时,如何提高查询性能以将 运行 时间减少到秒(而不是分钟)范围内?
尝试
select SUM(OrderThreshold < @LOW_COST) as LOW_COUNT,
SUM(OrderThreshold > @HIGH_COST) as HIGH_COUNT
from parts
where parttypeid = 1
and OrderThreshold not between @LOW_COST and @HIGH_COST
和
select count(*) as LOW_COUNT, null as HIGH_COUNT
from parts
where parttypeid = 1
and OrderThreshold < @LOW_COST
union all
select null, count(*)
from parts
where parttypeid = 1
and OrderThreshold > @HIGH_COST
您接受的答案没有解释您的原始查询出了什么问题:
select SUM(OrderThreshold < @LOW_COST) as LOW_COUNT,
SUM(OrderThreshold > @HIGH_COST) as HIGH_COUNT
from parts
where parttypeid = 1;
正在使用索引查找结果,但有很多行 parttypeid = 1
。我猜测每个数据页可能至少有一个这样的行。这意味着正在获取所有行,但它们被乱序读取。这比只进行完整的 table 扫描(如在第一个查询中)要慢。换句话说,正在读取所有数据页,但索引增加了额外的开销。
正如 Juergen 指出的那样,更好的查询形式将条件移动到 where
子句中:
select SUM(OrderThreshold < @LOW_COST) as LOW_COUNT,
SUM(OrderThreshold > @HIGH_COST) as HIGH_COUNT
from parts
where parttypeid = 1 AND
(OrderThreshold < @LOW_COST OR OrderThreshold > @HIGH_COST)
(我更喜欢这种形式,因为 where
条件与 case
条件匹配。)对于此查询,您需要 parts(parttypeid, OrderThreshold)
上的索引。在这种情况下,我不确定 MySQL 优化器,但最好写成:
select 'Low' as which, count(*) as CNT
from parts
where parttypeid = 1 AND
OrderThreshold < @LOW_COST
union all
select 'High', count(*) as CNT
from parts
where parttypeid = 1 AND
OrderThreshold > @HIGH_COST;
在这种情况下,每个子查询肯定应该使用索引。 (如果你想让它们排成两列,有几种方法可以实现,但我猜这不是那么重要。)
不幸的是,没有 where
子句的查询的最佳索引是 parts(OrderThreshold)
。这与上面的索引不同。