Question

I ai données hiérarchiques dans un modèle de série emboîtée (tableau: projets):

Mon tableau (projets):

id, lft, rgt
1, 1, 6
2, 2, 3
3, 4, 5
4, 7, 10
5, 8, 9
6, 11, 12
7, 13, 14
...

Jolie imprimé:

 1
  2
  3
 4
  5
 6
 7

Pour trouver le super nœud du nœud 3 (connaître sa valeur LFT) le plus proche, je peux faire

explain
SELECT projects.*
FROM projects
WHERE 4 BETWEEN projects.lft AND projects.rgt

Ce qui me donne une liste des projets dans le chemin vers le bas au noeud 3. Ensuite, en groupant et trouver MAX (projects.lft) des résultats, je reçois le super nœud le plus proche. Cependant, je ne peux pas sembler obtenir cette requête pour courir vite, il utilise l'habitude les indices que j'ai défini. EXPLIQUER dit:

+----+-------------+----------+-------+----------------+----------+---------+------+------+--------------------------+
| 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 comprend ce que l'indice à utiliser, mais doit encore boucler à travers toutes les 10 lignes (ou 100k dans ma table réelle).

Comment puis-je obtenir MySql pour optimiser cette requête correctement? J'include un script de test sous.

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
Était-ce utile?

La solution

Pour optimiser les requêtes imbriquées ensemble dans MySQL, vous devez créer un index de SPATIAL (de R-Tree) sur les boîtes de set:

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;

Voir cet article dans mon blog pour plus de détails:

Autres conseils

Si vous ne pouvez pas utiliser l'index spatial, ces deux indices:

ALTER TABLE projects ADD INDEX lftRgt (lft, rgt);
ALTER TABLE projects ADD INDEX idLftRgt (id, lft, rgt);

doit être unique. Cela aidera à la base de données beaucoup.

ALTER TABLE projects ADD INDEX lft (lft);

est pas nécessaire -. Il est un double de lftRgt

Nous sommes tombés sur ce tout en essayant de trouver de l'aide sur l'indexation des ensembles imbriqués.

J'atterris avec une autre solution, qui est volumineux mais facilement entièrement indexés. Cependant, il fera des mises à jour encore plus lent. Cependant, je suis annonce ici car il pourrait aider les autres.

Nous avons une table de catégories de produits, qui peuvent avoir des sous-catégories, etc. Ces données sont tout à fait statique.

configurer une table mise en mémoire cache les relations entre les catégories contenant la catégorie et une ligne pour chaque catégorie mère (y compris cette catégorie particulière), ainsi que la différence de profondeur.

Lorsqu'une modification est apportée à la table de catégorie réelle je déclenche juste une procédure pour reconstruire la table en cache.

Ensuite, tout ce qui est vérification de la relation parent / enfant peut simplement utiliser le cache pour relier directement entre une catégorie et tous ses enfants (ou un enfant et tous ses parents).

Le tableau de la catégorie actuelle.

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 ;

La table simple cache: -

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;

La procédure: -

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

Cela pourrait probablement être utilement améliorée si la table est grande et fréquemment mis à jour.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top