3

我有以下表格(删除了不相关的内容):

create table Payment (
    id int not null auto_increment,
    status int not null,
    primary key(id)
);
create table Booking (
    id int not null auto_increment,
    paymentId int not null,
    nrOfPassengers int not null,
    primary key(id),
    key paymentFK (paymentId),
    constraint paymentFK foreign key (paymentId) references Payment(id)
);

Booking包含 ~456k 行和Payment~331k 行。以下查询耗时 0.06 秒并返回 97 行:

select * from Booking b
join Payment p on b.paymentId = p.id
where p.status = 3

如果我添加一个order by子句,则查询需要 4.4 秒,几乎慢了 100 倍:

select * from Booking b
join Payment p on b.paymentId = p.id
where p.status = 3
order by b.nrOfPassengers

解释第一个查询:

id, select_type, table, type, possible_keys, key,       key_len, ref,  rows,   Extra
1,  SIMPLE,      p,     ALL,  PRIMARY,       NULL,      NULL,    NULL, 331299, Using where
1,  SIMPLE,      b,     ref,  paymentFK,     paymentFK, 9,       p.id, 1,      Using where

第二个:

id, select_type, table, type, possible_keys, key,       key_len, ref,  rows,   Extra
1,  SIMPLE,      p,     ALL,  PRIMARY,       NULL,      NULL,    NULL, 331299, Using where; Using temporary; Using filesort
1,  SIMPLE,      b,     ref,  paymentFK,     paymentFK, 9,       p.id, 1,      Using where

我使用 MySQL 5.1.34。

查询中使用的where子句从Payment. where我的印象是 MySQL 在使用(高度选择性)子句过滤结果集之前对结果集进行排序。我是对的吗?如果是这样,它为什么要这样做?我已经尝试分析这两个表,但查询计划没有变化。

4

2 回答 2

1

我有一个怀疑,问题是在你删除的不相关的东西中,有一个TEXTorBLOB列使 MySQL 去磁盘上存储临时表的中间结果。

无论如何,我们从执行计划中看到:对于Payment表中的每一行,从磁盘中获取它,检查条件,如果每个匹配行都为真,则Booking放入临时表中。使用所有数据对整个表进行排序nrOfPassengers并输出。如果有TextBlob字段,则中间表存储在磁盘上并排序,因为 MySQL 无法预测表的大小。

您可以做的(像往常一样)是最小化磁盘操作。正如@ajreal 建议的那样,在status列上添加一个索引。如果它如此有选择性,您将不需要任何其他索引,但如果您将其扩展paymentFK(paymentId, nrOfPassengers)它会更好。现在重写查询如下:

SELECT p.*, b.*
FROM (
  select p.id as paymentId, b.id as bookingId
  from Booking b
  join Payment p on b.paymentId = p.id
  where p.status = 3
  order by b.nrOfPassengers
) as ids
JOIN Payment p ON ids.paymentId = p.id
JOIN Booking b ON ids.bookingId = b.id;

数据将按子查询顺序输出。

于 2013-02-01T12:13:19.267 回答
1

首先,确保您的表上有适当的索引。假设您这样做并且它仍然比预期的要慢,您可以将结果放入子查询中而不对其进行排序,然后添加 ORDER BY 子句:

SELECT * 
FROM (
   select * from Booking b
   join Payment p on b.paymentId = p.id
   where p.status = 3
)
ORDER BY nrOfPassengers

我不确定这有多少(或是否)有帮助,因为当我查看执行计划时它会添加一行,但它可能会更快。

祝你好运。

于 2013-01-30T16:37:19.790 回答