【发布时间】:2017-07-11 05:09:15
【问题描述】:
我有一个带有嵌套集合模型的类别表。每行应包含其子类别的数量以及其中有多少文章,如果没有,则为“0”。
我已经搜索了 arround 并找到了两种可能的解决方案,但它们都不起作用:
MySQL & nested set: slow JOIN (not using index)
Why isn't MySQL using any of these possible keys?
创建表格类别:
CREATE TABLE `categories` (
`GROUP_ID` varchar(255) CHARACTER SET utf8 NOT NULL,
`GROUP_NAME` varchar(255) CHARACTER SET utf8 NOT NULL,
`PARENT_ID` varchar(255) CHARACTER SET utf8 NOT NULL,
`TYPE` enum('root','node','leaf') CHARACTER SET utf8 NOT NULL DEFAULT 'node',
`LEVEL` tinyint(2) NOT NULL DEFAULT '0',
`GROUP_ORDER` int(11) NOT NULL,
`GROUP_DESCRIPTION` text CHARACTER SET utf8 NOT NULL,
`total_articles` int(11) unsigned NOT NULL DEFAULT '0',
`total_cats` int(11) unsigned NOT NULL DEFAULT '0',
`lft` smallint(5) unsigned NOT NULL DEFAULT '0',
`rgt` smallint(5) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`GROUP_ID`),
KEY `PARENT_ID` (`PARENT_ID`),
KEY `lft` (`lft`),
KEY `rgt` (`rgt`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
total_cats 是行树中子类别的数量。
以下查询将完全符合我的要求:所有子类别和文章计数。 但是速度很慢。对约 5000 个类别和约 40000 篇文章执行 80 多秒。 total_articles 的计算已由另一个脚本完成。 (如果没有文章,则所有行都应为0 for total_articles)
查询:
SELECT a.GROUP_ID,a.PARENT_ID,COUNT(b.GROUP_ID) as total_cats,(
SELECT SUM(c.total_articles)
FROM categories c
WHERE c.PARENT_ID = a.GROUP_ID) as total_articles
FROM categories as b
INNER JOIN categories as a
ON a.lft < b.lft AND a.rgt > b.rgt
GROUP BY a.GROUP_ID
结果如下:
+-------------------------------------------+-------------------------------------+------------+----------------+
| GROUP_ID | PARENT_ID | total_cats | total_articles |
+-------------------------------------------+-------------------------------------+------------+----------------+
| 69_69_1 | 69_69_0 | 4252 | 0 |
| 69_69_Abfall__Wertstoffsammler___zubehoer | 69_69_NWEAB290h001 | 5 | 20 |
| 69_69_Abisolierzangen | 69_69_NWAAA458h001 | 4 | 56 |
| 69_69_Abzieher_2 | 69_69_NWAAB944h001 | 23 | 476 |
| 69_69_Abziehvorrichtung | 69_69_Abzieher_2 | 3 | 18 |
| 69_69_Aexte | 69_69_NWEAA615h001 | 6 | 45 |
| 69_69_Alarmgeraete_Melder | 69_69_Sicherungstechnik__Heimschutz | 3 | 4 |
| 69_69_Allgemeiner_Industriebedarf | 69_69_Industrieausruestung | 8 | 21 |
| 69_69_Allgemeines_Schweisszubehoer | 69_69_NWEAB113h001 | 27 | 97 |
| 69_69_Anker__Befestigungstechnik__1 | 69_69_Befestigungstechnik | 5 | 163 |
如果有帮助请解释:
+----+--------------------+-------+------+---------------+-----------+---------+------+------+------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------------+-------+------+---------------+-----------+---------+------+------+------------------------------------------------+
| 1 | PRIMARY | b | ALL | lft,rgt | NULL | NULL | NULL | 4253 | Using temporary; Using filesort |
| 1 | PRIMARY | a | ALL | lft,rgt | NULL | NULL | NULL | 4253 | Range checked for each record (index map: 0xC) |
| 2 | DEPENDENT SUBQUERY | c | ref | PARENT_ID | PARENT_ID | 767 | func | 7 | NULL |
+----+--------------------+-------+------+---------------+-----------+---------+------+------+------------------------------------------------+
如您所见,它不使用索引。如果我将FORCE INDEX (lft,rgt) 放在JOIN 旁边,查询会执行,但没有任何变化。还尝试在 lft 和 right 列上添加索引:
ALTER TABLE `categories` ADD INDEX `nestedset` (`lft`, `rgt`);
但这根本没有帮助。查询仍然很慢。
有趣的是:如果类别表只填充了少量行,例如,查询会非常快。 260. 但是如果达到 1000+ 就会越来越慢。
具有约 4000 个类别的示例数据:http://pastebin.com/BsViwFM5 这是一个大文件!
感谢您的帮助和提示!
【问题讨论】:
-
最好在 dba.stackexchange 上询问?
-
也许你是对的,但其他人问过类似的情况,所以如果有人想迁移它,请随时这样做 =)
-
顺便提一下,INT这个词无论出现在哪里,后面的数字都是毫无意义的
-
那我应该改用什么?最初 lft 和 rgt 列是 INT,所以我将其更改为 smallint,因此 mysql 不必使用完整的 INT-scale
-
我会始终坚持使用 INT - 但无论哪种方式,我怀疑它对性能有很大影响。您选择的 VARCHAR id(对于组和父级)可能会略微影响性能,但可能不会很大。
标签: mysql join indexing nested