简体   繁体   English

跨多个连接表的MySQL ORDER BY列

[英]MySQL ORDER BY columns across multiple joined tables

I have a database which consists of three tables, with the following structure: 我有一个由三个表组成的数据库,具有以下结构:

restaurant table: restaurant_id, location_id, rating. 餐厅餐桌: restaurant_id,location_id,评级。 Example: 1325, 77, 4.5 示例: 1325,77,4.5

restaurant_name table: restaurant_id, language, name. restaurant_name表: restaurant_id,语言,名称。 Example: 1325, 'en', 'Pizza Express' 示例: 1325,'en','Pizza Express'

location_name table: location_id, language, name. location_name表: location_id,语言,名称。 Example: 77, 'en', 'New York' 例如: 77,'en','纽约'

I would like to get the restaurant info in English, sorted by location name and restaurant name, and use the LIMIT clause to paginate the result. 我想用英语获取餐厅信息,按地点名称和餐馆名称排序,并使用LIMIT子句对结果进行分页。 So my SQL is: 所以我的SQL是:

SELECT ln.name, rn.name
FROM restaurant r
INNER JOIN location_name ln
ON r.location_id = ln.location_id
AND ln.language = 'en'
INNER JOIN restaurant_name rn
ON r.restaurant_id = rn.restaurant_id
AND rn.language = 'en'
ORDER BY ln.name, rn.name
LIMIT 0, 50

This is terribly slow - so I refined my SQL with deferred JOIN, which make things a lot faster (from over 10 seconds to 2 seconds): 这非常慢 - 所以我使用延迟JOIN来优化我的SQL,这使事情变得更快(从10秒到2秒):

SELECT ln.name, rn.name
FROM restaurant r
INNER JOIN (
    SELECT r.restaurant_id
    FROM restaurant r
    INNER JOIN location_name ln
    ON r.location_id = ln.location_id
    AND ln.language = 'en'
    INNER JOIN restaurant_name rn
    ON r.restaurant_id = rn.restaurant_id
    AND rn.language = 'en'
    ORDER BY ln.name, rn.name
    LIMIT 0, 50
) r1
ON r.restaurant_id = r1.restaurant_id
INNER JOIN location_name ln
ON r.location_id = ln.location_id
AND ln.language = 'en'
INNER JOIN restaurant_name rn
ON r.restaurant_id = rn.restaurant_id
AND rn.language = 'en'
ORDER BY ln.name, rn.name

2 seconds is unfortunately still not very acceptable to the user, so I go and check the EXPLAIN of the my query, and it appears that the slow part is on the ORDER BY clause, which I see "Using temporary; Using filesort". 遗憾的是,2秒对于用户来说仍然不是很容易接受,所以我去检查我的查询的EXPLAIN,看起来缓慢的部分是在ORDER BY子句上,我看到“使用临时;使用filesort”。 I checked the official reference manual about ORDER BY optimization and I come across this statement: 我检查了关于ORDER BY优化的官方参考手册,我遇到了这个声明:

In some cases, MySQL cannot use indexes to resolve the ORDER BY, although it may still use indexes to find the rows that match the WHERE clause. 在某些情况下,MySQL不能使用索引来解析ORDER BY,尽管它仍然可以使用索引来查找与WHERE子句匹配的行。 Examples: 例子:

The query joins many tables, and the columns in the ORDER BY are not all from the first nonconstant table that is used to retrieve rows. 查询连接了许多表,ORDER BY中的列并非全部来自用于检索行的第一个非常量表。 (This is the first table in the EXPLAIN output that does not have a const join type.) (这是EXPLAIN输出中第一个没有const连接类型的表。)

So for my case, given that the two columns I'm ordering by are from the nonconstant joined tables, index cannot be used. 所以对于我的情况,假设我订购的两列来自非常量连接表,则不能使用索引。 My question is, is there any other approach I can take to speed things up, or what I've done so far is already the best I can achieve? 我的问题是,我可以采取任何其他方法来加快速度,或者到目前为止我所做的已经是我能做到的最好的方法了吗?

Thanks in advance for your help! 在此先感谢您的帮助!

EDIT 1 编辑1

Below is the EXPLAIN output with the ORDER BY clause: 下面是带有ORDER BY子句的EXPLAIN输出:

+----+-------------+------------+--------+--------------------------+-----------------------+---------+--------------------------------+------+----------------------------------------------+
| id | select_type | table      | type   | possible_keys            | key                   | key_len | ref                            | rows | Extra                                        |
+----+-------------+------------+--------+--------------------------+-----------------------+---------+--------------------------------+------+----------------------------------------------+
|  1 | PRIMARY     | <derived2> | ALL    | NULL                     | NULL                  | NULL    | NULL                           |   50 |                                              |
|  1 | PRIMARY     | rn         | ref    | idx_restaurant_name_1    | idx_restaurant_name_1 | 1538    | r1.restaurant_id,const,const   |    1 | Using where                                  |
|  1 | PRIMARY     | r          | eq_ref | PRIMARY,idx_restaurant_1 | PRIMARY               | 4       | r1.restaurant_id               |    1 |                                              |
|  1 | PRIMARY     | ln         | ref    | idx_location_name_1      | idx_location_name_1   | 1538    | test.r.location_id,const,const |    1 | Using where                                  |
|  2 | DERIVED     | rn         | ALL    | idx_restaurant_name_1    | NULL                  | NULL    | NULL                           | 8484 | Using where; Using temporary; Using filesort |
|  2 | DERIVED     | r          | eq_ref | PRIMARY,idx_restaurant_1 | PRIMARY               | 4       | test.rn.restaurant_id          |    1 |                                              |
|  2 | DERIVED     | ln         | ref    | idx_location_name_1      | idx_location_name_1   | 1538    | test.r.location_id             |    1 | Using where                                  |
+----+-------------+------------+--------+--------------------------+-----------------------+---------+--------------------------------+------+----------------------------------------------+

Below is the EXPLAIN output without the ORDER BY clause: 下面是没有ORDER BY子句的EXPLAIN输出:

+----+-------------+------------+--------+--------------------------+-----------------------+---------+--------------------------------+------+--------------------------+
| id | select_type | table      | type   | possible_keys            | key                   | key_len | ref                            | rows | Extra                    |
+----+-------------+------------+--------+--------------------------+-----------------------+---------+--------------------------------+------+--------------------------+
|  1 | PRIMARY     | <derived2> | ALL    | NULL                     | NULL                  | NULL    | NULL                           |   50 |                          |
|  1 | PRIMARY     | rn         | ref    | idx_restaurant_name_1    | idx_restaurant_name_1 | 1538    | r1.restaurant_id,const,const   |    1 | Using where              |
|  1 | PRIMARY     | r          | eq_ref | PRIMARY,idx_restaurant_1 | PRIMARY               | 4       | r1.restaurant_id               |    1 |                          |
|  1 | PRIMARY     | ln         | ref    | idx_location_name_1      | idx_location_name_1   | 1538    | test.r.location_id,const,const |    1 | Using where              |
|  2 | DERIVED     | rn         | index  | idx_restaurant_name_1    | idx_restaurant_name_1 | 1538    | NULL                           | 8484 | Using where; Using index |
|  2 | DERIVED     | r          | eq_ref | PRIMARY,idx_restaurant_1 | PRIMARY               | 4       | test.rn.restaurant_id          |    1 |                          |
|  2 | DERIVED     | ln         | ref    | idx_location_name_1      | idx_location_name_1   | 1538    | test.r.location_id             |    1 | Using where; Using index |
+----+-------------+------------+--------+--------------------------+-----------------------+---------+--------------------------------+------+--------------------------+

EDIT 2 编辑2

Below are the DDL of the table. 下面是表格的DDL。 I built them for illustrating this problem only, the real table has much more columns. 我构建它们只是为了说明这个问题,真正的表有更多的列。

CREATE TABLE restaurant (
  restaurant_id INT NOT NULL AUTO_INCREMENT,
  location_id INT NOT NULL,
  rating INT NOT NULL,
  PRIMARY KEY (restaurant_id),
  INDEX idx_restaurant_1 (location_id)
);

CREATE TABLE restaurant_name (
  restaurant_id INT NOT NULL,
  language VARCHAR(255) NOT NULL,
  name VARCHAR(255) NOT NULL,
  INDEX idx_restaurant_name_1 (restaurant_id, language),
  INDEX idx_restaurant_name_2 (name)
);

CREATE TABLE location_name (
  location_id INT NOT NULL,
  language VARCHAR(255) NOT NULL,
  name VARCHAR(255) NOT NULL,
  INDEX idx_location_name_1 (location_id, language),
  INDEX idx_location_name_2 (name)
);

Based on the EXPLAIN numbers, there could be about 170 "pages" of restaurants (8484/50)? 根据EXPLAIN号码,可能有大约170页的餐馆(8484/50)? I suggest that that is impractical for paging through. 我建议这对于翻阅是不切实际的。 I strongly recommend you rethink the UI. 我强烈建议您重新考虑用户界面。 In doing so, the performance problem you state will probably vanish. 这样做,您声明的性能问题可能会消失。

For example, the UI could be 2 steps instead of 170 to get to the restaurants in Zimbabwe. 例如,UI可以是2步而不是170步到达津巴布韦的餐馆。 Step 1, pick a country. 第1步,选择一个国家。 (OK, that might be page 5 of the countries.) Step 2, view the list of restaurants in that country; (好的,这可能是各国的第5页。)步骤2,查看该国家/地区的餐馆列表; it would be only a few pages to flip through. 翻阅只有几页。 Much better for the user; 对用户来说好多了; much better for the database. 更好的数据库。

Addenda 附加物

In order to optimize the pagination, get the paginated list of pages from a single table (so that you can 'remember where you left off'). 为了优化分页,从单个表中获取分页的页面列表(这样您就可以“记住中断的位置”)。 Then join the language table(s) to look up the translations. 然后加入语言表以查找翻译。 Note that this only looks up on page's worth of translations, not thousands. 请注意,这仅查看页面的翻译价值,而不是数千。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM