使用Order By子句优化JOINED结果

时间:2017-12-01 16:33:15

标签: mysql query-optimization

考虑以下架构(sqlFiddle上提供)

create table ad (
  id int primary key auto_increment,
  category_id int,
  city_id int,
  name varchar(255),
  key(category_id),
  key(city_id)
);

create table category (
  id int primary key auto_increment,
  name varchar(255)
);

create table city (
  id int primary key auto_increment,
  name varchar(255)
);

insert into category values (null, 'Category 1');
insert into category values (null, 'Category 2');
insert into category values (null, 'Category 3');

insert into city values (null, 'City 1');
insert into city values (null, 'City 2');

insert into ad values (null, 1, 1, "Category 1 city 1");
insert into ad values (null, 1, 2, "Category 1 city 2");
insert into ad values (null, 2, 1, "Category 2 city 1");
insert into ad values (null, 2, 2, "Category 2 city 2");
insert into ad values (null, 3, 1, "Category 3 city 1");
insert into ad values (null, 3, 2, "Category 3 city 2");

执行简单连接查询时没有任何顺序:

SELECT ad.id, ad.name, category.name, city.name FROM ad
INNER JOIN category ON category.id = ad.category_id
INNER JOIN city ON city.id = ad.city_id

结果非常有效:

enter image description here

但是,只要我添加ORDER BY子句,就会涉及临时表和文件排序:

SELECT ad.id, ad.name, category.name, city.name FROM ad
INNER JOIN category ON category.id = ad.category_id
INNER JOIN city ON city.id = ad.city_id
ORDER BY ad.id

enter image description here

如何优化此类查询?

3 个答案:

答案 0 :(得分:0)

您可能想要使用STRAIGTH_JOIN。

SELECT STRAIGHT_JOIN ad.id, ad.name, category.name, city.name FROM ad
INNER JOIN category ON category.id = ad.category_id
INNER JOIN city ON city.id = ad.city_id
ORDER BY ad.id

MySQL优化器选择以错误的顺序(城市,广告,类别)访问表格,最佳访问顺序(广告,类别,城市)STRAIGTH_JOIN将强制访问表顺序。

答案 1 :(得分:0)

过早的恐慌。由于行数太少,EXPLAIN不会“证明”查询计划不好。凭借一千个广告和几十个城市和类别,优化工具可以选择ad作为第一个使用的表格。

此外,优化程序不知道您的表格是1个广告到多个类别和城市。或许多:很多。

您有抱怨,因为您知道每个ad只在一个类别和一个城市中?

“BNL”和“使用连接缓冲区”是执行查询的非常有效的方法 - 它们加载所有内容,然后在RAM中以有效的方式对其进行操作。

此外,“使用临时”和“使用文件输出”并不像听起来那么糟糕。这通常是在RAM中完成的,具有高效的内存“qsort”。

答案 2 :(得分:-1)

我设法找到了自己的解决方案。首先,需要从主表中有效地计算所需的ID(对它们进行过滤和排序),然后再次将结果与内部查询中的键相连:

SELECT ad.id, ad.name, category.name, city.name FROM
    (
        SELECT id FROM ad WHERE price <= 3000 ORDER BY id DESC
    ) AS v
JOIN ad ON v.id = ad.id
JOIN category ON category.id = ad.category_id
JOIN city ON city.id = ad.city_id

当然,我的案例中涉及更多专栏。使用这样的查询(在主表中有60k记录),执行速度从0.16s增加到0.004s,只要我按索引列排序。