使用 Order By 子句优化 JOINED 结果

Posted

技术标签:

【中文标题】使用 Order By 子句优化 JOINED 结果【英文标题】:Optimizing JOINED results with Order By clause 【发布时间】:2017-12-01 16:33:15 【问题描述】:

考虑以下架构(在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

结果相当高效:

但是,一旦我添加了 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

如何优化这样的查询?

【问题讨论】:

如果您需要排序,则需要一个临时表来构建要排序的值,然后对结果值进行排序......问题在哪里?????? 感谢您的宝贵意见。 【参考方案1】:

您可能想要使用 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 / 3 还不错。 STRAIGHT_JOIN 今天工作得很好;明天它可能会强制执行一个不太有利的命令。【参考方案2】:

过早的恐慌。由于行数如此之少,EXPLAIN 不会“证明”查询计划不好。对于一千个广告和数十个城市和类别,优化器可能会选择 ad 作为第一个使用的表。

此外,优化器不知道您的表格是否是一对多类别和城市的广告。或者很多:很多。

你抱怨是因为你知道每个ad只属于一个类别和一个城市?

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

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

【讨论】:

我有一个包含数千个广告的生产表,并且查询确实产生了相同的结果。我已经设法优化了这个查询并将我的解决方案发布为明天的答案。 @mike,请分享您的解决方案,以便每个人都可以从中学习:)【参考方案3】:

我自己设法找到了解决方案。首先,需要从主表中有效地计算所需的 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.16 秒提高到 0.004 秒。

【讨论】:

以上是关于使用 Order By 子句优化 JOINED 结果的主要内容,如果未能解决你的问题,请参考以下文章

Mysql之order by|group by 排序优化

优化 sum() over(order by...) 子句抛出“超出资源”错误

查询优化--ORDER BY查询优化

oracle中group by 和order by 同时用

UNION ALL、UNION与ORDER BY

oracle group by 性能优化