Mysql:优化在嵌套集树中查找超级节点
我在嵌套集模型中有层次数据(表:项目): 我的表格(项目): 印刷精美:Mysql:优化在嵌套集树中查找超级节点,sql,mysql,nested-sets,Sql,Mysql,Nested Sets,我在嵌套集模型中有层次数据(表:项目): 我的表格(项目): 印刷精美: 1 2 3 4 5 6 7 为了找到节点3最近的超级节点(知道它的lft值),我可以 explain SELECT projects.* FROM projects WHERE 4 BETWEEN projects.lft AND projects.rgt 这给了我一个到节点3的路径中的项目列表。然后通过分组和查找结果的MAX(projects.lft),我得到最近的超级节点。然而,我似乎无法让这个
1
2
3
4
5
6
7
为了找到节点3最近的超级节点(知道它的lft值),我可以
explain
SELECT projects.*
FROM projects
WHERE 4 BETWEEN projects.lft AND projects.rgt
这给了我一个到节点3的路径中的项目列表。然后通过分组和查找结果的MAX(projects.lft),我得到最近的超级节点。然而,我似乎无法让这个查询快速运行,它不会使用我定义的索引。解释说:
+----+-------------+----------+-------+----------------+----------+---------+------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------+-------+----------------+----------+---------+------+------+--------------------------+
| 1 | SIMPLE | projects | index | lft,rgt,lftRgt | idLftRgt | 12 | NULL | 10 | Using where; Using index |
+----+-------------+----------+-------+----------------+----------+---------+------+------+--------------------------+
Mysql知道要使用什么索引,但仍然必须循环遍历所有10行(或实际表中的100k行)
如何让MySql正确优化此查询?我在下面包含了一个测试脚本
DROP TABLE IF EXISTS projects;
CREATE TABLE projects (
id INT NOT NULL ,
lft INT NOT NULL ,
rgt INT NOT NULL ,
PRIMARY KEY ( id )
) ENGINE = MYISAM ;
ALTER TABLE projects ADD INDEX lft (lft);
ALTER TABLE projects ADD INDEX rgt (rgt);
ALTER TABLE projects ADD INDEX lftRgt (lft, rgt);
ALTER TABLE projects ADD INDEX idLftRgt (id, lft, rgt);
INSERT INTO projects (id,lft,rgt) VALUES (1,1,6);
INSERT INTO projects (id,lft,rgt) VALUES (2,2,3);
INSERT INTO projects (id,lft,rgt) VALUES (3,4,5);
INSERT INTO projects (id,lft,rgt) VALUES (4,7,10);
INSERT INTO projects (id,lft,rgt) VALUES (5,8,9);
INSERT INTO projects (id,lft,rgt) VALUES (6,11,12);
INSERT INTO projects (id,lft,rgt) VALUES (7,13,14);
INSERT INTO projects (id,lft,rgt) VALUES (8,15,16);
INSERT INTO projects (id,lft,rgt) VALUES (9,17,18);
INSERT INTO projects (id,lft,rgt) VALUES (10,19,20);
explain
SELECT projects.*
FROM projects
WHERE 4 BETWEEN projects.lft AND projects.rgt
要优化
MySQL
中的嵌套集合查询,应在集合框上创建SPATIAL
(R-Tree
)索引:
ALTER TABLE projects ADD sets LINESTRING;
UPDATE projects
SET sets = LineString(Point(-1, lft), Point(1, rgt));
ALTER TABLE projects MODIFY sets LINESTRING NOT NULL;
CREATE SPATIAL INDEX sx_projects_sets ON projects (sets);
SELECT hp.*
FROM projects hp
WHERE MBRWithin(Point(0, 4), hp.sets)
ORDER BY
lft;
有关更多详细信息,请参阅我博客中的这篇文章:
ALTER TABLE projects ADD INDEX lftRgt (lft, rgt);
ALTER TABLE projects ADD INDEX idLftRgt (id, lft, rgt);
应该是独一无二的。这将对数据库有很大帮助
ALTER TABLE projects ADD INDEX lft (lft);
不是必需的-它是lftRgt的副本。在尝试查找嵌套集索引帮助时遇到了此问题 我得到了一个不同的解决方案,它体积庞大,但很容易完全索引。但是,它会使更新速度更慢。然而,我把它贴在这里,因为它可能会帮助其他人 我们有一个产品类别表,可以有子类别等。这些数据是相当静态的 我设置了一个表,缓存包含该类别的类别之间的关系以及每个父类别(包括这个特定类别)的行,以及深度差异 当对实际的category表进行更改时,我只是触发一个过程来重建缓存表 然后,检查父/子关系的任何内容都可以使用缓存在类别及其所有子级(或子级及其所有父级)之间直接链接 实际类别表
CREATE TABLE `category` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(128) NOT NULL,
`depth` int(11) NOT NULL,
`left_index` int(4) NOT NULL,
`right_index` int(4) NOT NULL,
`mmg_code` varchar(30) NOT NULL
PRIMARY KEY (`id`),
UNIQUE KEY `mmg_code` (`mmg_code`),
UNIQUE KEY `left_index_right_index` (`left_index`,`right_index`),
UNIQUE KEY `depth_left_index_right_index` (`depth`,`left_index`,`right_index`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
DELIMITER ;;
CREATE TRIGGER `category_ai` AFTER INSERT ON `category` FOR EACH ROW
CALL `proc_rebuild_category_parents_cache`();;
CREATE TRIGGER `category_au` AFTER UPDATE ON `category` FOR EACH ROW
CALL `proc_rebuild_category_parents_cache`();;
DELIMITER ;
简单缓存表:-
CREATE TABLE `category_parents_cache` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`category_id` int(11) NOT NULL,
`parent_category_id` int(11) NOT NULL,
`depth_difference` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `category_id` (`category_id`),
KEY `parent_category_id` (`parent_category_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
程序如下:-
BEGIN
TRUNCATE category_parents_cache;
INSERT INTO category_parents_cache (id, category_id, parent_category_id, depth_difference)
SELECT NULL,
child_category.id AS category_id,
category.id AS parent_category_id,
child_category.depth - category.depth AS depth_difference
FROM category
INNER JOIN category child_category ON child_category.left_index BETWEEN category.left_index AND category.right_index
ORDER BY category.id, child_category.id;
END
如果表格很大并且经常更新,这可能会得到有效的改进。我的朋友,你真是个天才!你刚刚救了我们提前退休的db服务器。当我们制作一张信用卡时,您将进入信用卡列表(yast.com):)
BEGIN
TRUNCATE category_parents_cache;
INSERT INTO category_parents_cache (id, category_id, parent_category_id, depth_difference)
SELECT NULL,
child_category.id AS category_id,
category.id AS parent_category_id,
child_category.depth - category.depth AS depth_difference
FROM category
INNER JOIN category child_category ON child_category.left_index BETWEEN category.left_index AND category.right_index
ORDER BY category.id, child_category.id;
END