首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Mysql:在嵌套集树中优化查找超级节点

Mysql:在嵌套集树中优化查找超级节点
EN

Stack Overflow用户
提问于 2009-11-16 18:12:33
回答 3查看 3K关注 0票数 9

嵌套集模型(表:项目)中有分层数据:

我的表格(项目):

代码语言:javascript
复制
id, lft, rgt
1, 1, 6
2, 2, 3
3, 4, 5
4, 7, 10
5, 8, 9
6, 11, 12
7, 13, 14
...

印刷精美:

代码语言:javascript
复制
 1
  2
  3
 4
  5
 6
 7

要找到节点3最近的超级节点(知道其lft值),我可以这样做。

代码语言:javascript
复制
explain
SELECT projects.*
FROM projects
WHERE 4 BETWEEN projects.lft AND projects.rgt

它给出了到节点3的路径上的项目列表,然后通过分组和查找结果的最大值(projects.lft),得到最近的超级节点。但是,我似乎无法让这个查询快速运行,它不会使用我定义的索引。解释说:

代码语言:javascript
复制
+----+-------------+----------+-------+----------------+----------+---------+------+------+--------------------------+
| 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行(或实际表中的100 k)。

如何使MySql能够正确地优化这个查询?下面包含一个测试脚本。

代码语言:javascript
复制
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
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2009-11-16 18:25:26

要优化MySQL中的嵌套集查询,应该在set框上创建一个SPATIAL (R-Tree)索引:

代码语言:javascript
复制
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;

有关更多细节,请参阅我博客中的这篇文章:

票数 11
EN

Stack Overflow用户

发布于 2011-03-17 13:06:30

如果不能使用空间索引,那么这两个索引:

代码语言:javascript
复制
ALTER TABLE projects ADD INDEX lftRgt (lft, rgt);
ALTER TABLE projects ADD INDEX idLftRgt (id, lft, rgt);

应该是独一无二的。这对数据库有很大的帮助。

代码语言:javascript
复制
ALTER TABLE projects ADD INDEX lft (lft);

这是不必要的--它是lftRgt的复制。

票数 1
EN

Stack Overflow用户

发布于 2016-06-17 15:31:39

在为嵌套集寻找索引帮助时,遇到了这种情况。

我得到了一个不同的解决方案,它体积大,但很容易完全索引。然而,它将使更新速度更慢。然而,我在这里张贴它,因为它可能会帮助别人。

我们有一个产品分类表,它可以有子类别等等。这个数据是相当静态的。

我设置了一个表,缓存包含类别和每个父类别(包括这个特定类别)的行之间的关系,以及深度上的差异。

当对实际的类别表进行更改时,我只需要触发一个过程来重建缓存的表。

然后,任何正在检查父/子关系的东西都可以使用缓存直接链接到一个类别和它的所有子类(或一个子类及其所有父级)之间。

实际类别表。

代码语言:javascript
复制
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 ;

简单的缓存表:-

代码语言:javascript
复制
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;

程序:-

代码语言:javascript
复制
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

如果表很大且通常更新,这可能会得到有益的改进。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/1743894

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档