使用 GROUP BY、ORDER BY 和 GROUP_CONCAT 进行索引

Jus*_*tin 5 mysql group-by sql-order-by

解决见下文

我正在尝试同时使用GROUP BY和ORDER BY在我的查询中检索按难度排序的数据。我必须使用 ,GROUP BY因为GROUP CONCAT某些表(例如“lookup_peripheral”)将多个值链接到同一个键 (content_id)。我理解为什么 MYSQL 在执行此任务时不能使用索引,因为 GROUP BY 和 ORDER BY 语句不共享相同的字段。但是,我正在寻找不需要一天即可检索结果的替代解决方案。

如果省略GROUP BYorORDER BY子句,则数据库使用索引,但结果要么缺少所有外围设备,要么未按难度排序。

我在 'lookup_difficulty' 表中FROM使用,所以我可以使用该索引对结果进行排序。这些lookup_xxxxx表存储每个允许的值,然后是其他表,例如peripheral通过 content_id 将提交链接到值。一切都参考了提交content_id。该content表包含基本信息,例如成员 ID、姓名等。

如果我的帖子不够清楚,我深表歉意。

mysql> describe peripheral;
+------------------+----------+------+-----+---------+-------+
| Field            | Type     | Null | Key | Default | Extra |
+------------------+----------+------+-----+---------+-------+
| peripheral_id    | int(2)   | NO   | PRI | NULL    |       |
| peripheral       | char(30) | NO   |     | NULL    |       |
| peripheral_total | int(5)   | NO   |     | NULL    |       |
+------------------+----------+------+-----+---------+-------+

mysql> select * from peripheral;
+---------------+-----------------+------------------+
| peripheral_id | peripheral      | peripheral_total |
+---------------+-----------------+------------------+
|             1 | periph 1        |                0 |
|             2 | periph 2        |                1 |
|             3 | periph 3        |                3 |
+---------------+-----------------+------------------+
Run Code Online (Sandbox Code Playgroud)

:

mysql> describe lookup_peripheral;
+---------------+---------+------+------+---------+-------+
| Field         | Type    | Null | Key  | Default | Extra |
+---------------+---------+------+------+---------+-------+
| content_id    | int(10) | NO   | INDEX| NULL    |       |
| peripheral_id | int(2)  | NO   |      | NULL    |       |
+---------------+---------+------+------+---------+-------+  


mysql> mysql> select * from lookup_peripheral;
+------------+---------------+
| content_id | peripheral_id |
+------------+---------------+
|         74 |             2 |
|         74 |             5 |
|         75 |             2 |
|         75 |             5 |
|         76 |             3 |
|         76 |             4 |
+------------+---------------+
Run Code Online (Sandbox Code Playgroud)

以下不是在 lookup_difficulty 上使用索引,而是使用表排序和临时表。

SELECT group_concat(DISTINCT peripheral.peripheral) as peripheral, content.member, .....
FROM (lookup_difficulty)
LEFT OUTER JOIN lookup_peripheral ON lookup_difficulty.content_id = lookup_peripheral.content_id
LEFT OUTER JOIN peripheral ON peripheral.peripheral_id = lookup_peripheral.peripheral_id
.....
LEFT OUTER JOIN programmer ON programmer.programmer_id = lookup_programmer.programmer_id
LEFT OUTER JOIN lookup_programming_language ON lookup_difficulty.content_id = lookup_programming_language.content_id

GROUP BY lookup_difficulty.content_id
ORDER BY lookup_dfficulty.difficulty_id
LIMIT 30    
Run Code Online (Sandbox Code Playgroud)

最终目标是使用连接的正确外围设备检索按难度排序的结果。我想我需要一个子查询来实现这一点。


编辑:回答如下:

弄清楚了。我做了我怀疑我必须做的事情,即添加子查询。由于MYSQL只能使用每个表中的一个指数,我无法GROUP BY和SORT BY在一起,我特别的设置。相反,我添加了另一个查询,该查询将使用不同表上的另一个索引将外围设备组合在一起。这是我在SELECT上面的声明中添加的内容:

(SELECT group_concat(DISTINCT peripheral.peripheral) as peripheral
FROM lookup_peripheral
LEFT OUTER JOIN peripheral ON peripheral.peripheral_id = lookup_peripheral.peripheral_id
WHERE lookup_difficulty.content_id = lookup_peripheral.content_id
GROUP BY lookup_peripheral.content_id
LIMIT 1) as peripheral
Run Code Online (Sandbox Code Playgroud)

我使用了 aLEFT OUTER因为有些条目没有任何外围设备。对于大多数表的 40k 行数据库,在 400MHz 处理器上的总查询时间现在为 0.02 秒,100Hz RAM 为 128MB。

EXPLAIN现在给我USING INDEX的lookup_difficulty表。我添加了这个来实现:

ALTER TABLE `pictuts`.`lookup_difficulty` DROP PRIMARY KEY ,
ADD PRIMARY KEY ( `difficulty_id` , `content_id` ) 
Run Code Online (Sandbox Code Playgroud)

编辑 2 我注意到使用分页偏移较大时,页面加载速度会慢很多。您可能在其他网站上也遇到过这种情况。幸运的是,正如Peter Zaitsev所指出的那样,有一种方法可以避免这种情况。这是我更新的片段,用于实现 30K 或 0 偏移量的相同计时:

FROM 
SELECT lookup_difficulty.content_id, lookup_difficulty.difficulty_id
FROM lookup_difficulty
LIMIT '.$offset.', '.$per_page.'
) ld
Run Code Online (Sandbox Code Playgroud)

现在只需添加ld.whatever到每一个JOIN制作中,你就拥有了!我的查询现在看起来一团糟,但至少它是优化的。我认为没有人会在阅读这篇文章时走到这一步......

Joh*_*ica 4

加上贾斯汀的回答,这个问题就从未回答的列表中消失了:

弄清楚了。我做了我怀疑我必须做的事情,那就是添加一个子查询。由于 MYSQL 每个表只能使用一个索引,因此我无法针对我的特定设置同时使用 GROUP BY 和 SORT BY。相反,我添加了另一个查询,该查询将使用不同表上的另一个索引将外围设备分组在一起。这是我在上面的 SELECT 语句中添加的内容:

(SELECT group_concat(DISTINCT p.peripheral) as peripheral
FROM lookup_peripheral lp
LEFT JOIN peripheral p ON p.peripheral_id = lp.peripheral_id
WHERE ld.content_id = lp.content_id
GROUP BY lp.content_id
LIMIT 1) as peripheral
Run Code Online (Sandbox Code Playgroud)

我使用了 LEFT OUTER,因为有些条目没有任何外围设备。现在,对于大多数表的 40k 行数据库,在具有 128MB 100Hz RAM 的 400MHz 处理器上,总查询时间为 0.02 秒。

EXPLAIN 现在为我提供了lookup_difficulty 表的USING INDEX。我添加了这个来实现这个目的:

ALTER TABLE pictuts.lookup_difficulty DROP PRIMARY KEY ,
ADD PRIMARY KEY ( difficulty_id , content_id ) 
Run Code Online (Sandbox Code Playgroud)

编辑2 我注意到,使用分页进行大偏移量时,页面加载速度会相当慢。您可能在其他网站上也经历过这种情况。幸运的是,正如 Peter Zaitsev 指出的那样,有一种方法可以避免这种情况。这是我更新的代码片段,用于实现 30K 或 0 偏移量的相同计时:

FROM 
SELECT ld.content_id, ld.difficulty_id
FROM lookup_difficulty ld
LIMIT '.$per_page.' OFFSET '.$offset.' 
) ld
Run Code Online (Sandbox Code Playgroud)

现在只需将 ld.whatever 添加到每个 JOIN 中即可!我的查询现在看起来一团糟,但至少它得到了优化。我认为没有人能读到这里……