跨多个连接表

时间:2017-03-18 04:41:44

标签: mysql sql join indexing sql-order-by

我有一个由三个表组成的数据库,结构如下:

餐厅餐桌: restaurant_id,location_id,评分。 示例: 1325,77,4.5

restaurant_name表: restaurant_id,语言,名称。 示例: 1325,' en',' Pizza Express'

location_name表: location_id,语言,名称。 示例: 77,' en','纽约'

我想用英语获取餐厅信息,按地点名称和餐馆名称排序,并使用LIMIT子句对结果进行分页。所以我的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

这非常慢 - 所以我使用延迟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秒仍然不是用户可以接受的,所以我去检查我的查询的EXPLAIN,看起来缓慢的部分是在ORDER BY子句上,我看到"使用临时;使用filesort"。我查看了关于ORDER BY optimization的官方参考手册,我发现了这个陈述:

  

在某些情况下,MySQL无法使用索引来解析ORDER BY,   虽然它仍然可以使用索引来查找匹配的行   WHERE子句。例子:

     

查询连接了许多表,而ORDER BY中的列则没有   所有这些都来自用于检索行的第一个非常量表。   (这是EXPLAIN输出中第一个没有的表   const连接类型。)

因此,对于我的情况,假设我排序的两列来自非常量连接表,则不能使用索引。我的问题是,我可以采取其他方法来加快速度,或者到目前为止我所做的已经是我能做到的最好的了吗?

提前感谢您的帮助!

编辑1

以下是带有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                                  |
+----+-------------+------------+--------+--------------------------+-----------------------+---------+--------------------------------+------+----------------------------------------------+

以下是没有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 |
+----+-------------+------------+--------+--------------------------+-----------------------+---------+--------------------------------+------+--------------------------+

编辑2

以下是表格的DDL。我构建它们只是为了说明这个问题,真正的表有更多的列。

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)
);

1 个答案:

答案 0 :(得分:0)

根据EXPLAIN数字,可能有大约170页的餐馆(8484/50)?我建议这对于翻阅是不切实际的。我强烈建议您重新考虑用户界面。这样做,您声明的性能问题可能会消失。

例如,UI可以是2步而不是170步到达津巴布韦的餐馆。第1步,选择一个国家。 (好的,这可能是各国的第5页。)步骤2,查看该国家/地区的餐馆列表;翻阅只有几页。对用户来说好多了;更好的数据库。

<强>附加物

为了优化分页,请从单个表中获取分页的页面列表(这样您就可以“记住中断的位置”)。 然后加入语言表以查找翻译。请注意,这仅查看页面的翻译价值,而不是数千。