使用 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 结果的主要内容,如果未能解决你的问题,请参考以下文章