9

ネストされたセット モデル (テーブル:プロジェクト) に階層データがあります。

私のテーブル(プロジェクト):

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

プリティプリント:

 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) をグループ化して検索することにより、最も近いスーパー ノードを取得します。ただし、このクエリを高速に実行できないようです。定義したインデックスは使用されません。EXPLAIN 言います:

+----+-------------+----------+-------+----------------+----------+---------+------+------+--------------------------+
| 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
4

3 に答える 3

11

でネストされたセット クエリを最適化するには、セット ボックスに( ) インデックスMySQLを作成する必要があります。SPATIALR-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;

詳細については、私のブログのこの記事を参照してください。

于 2009-11-16T18:25:26.573 に答える
1

空間インデックスを使用できない場合は、次の 2 つのインデックスを使用します。

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 の複製です。

于 2011-03-17T13:06:30.853 に答える
0

ネストされたセットのインデックス作成に関するヘルプを見つけようとしているときに、これに遭遇しました。

私は、かさばるが簡単に完全にインデックス化できる別のソリューションにたどり着きました。ただし、更新はさらに遅くなります。ただし、他の人に役立つ可能性があるため、ここに投稿します。

サブカテゴリなどを持つことができる製品カテゴリのテーブルがあります。このデータは非常に静的です。

深さの違いとともに、カテゴリと各親カテゴリ (この特定のカテゴリを含む) の行を含むカテゴリ間の関係をキャッシュするテーブルを設定します。

実際のカテゴリ テーブルに変更が加えられたら、キャッシュされたテーブルを再構築する手順をトリガーするだけです。

次に、親子関係をチェックしているものはすべて、キャッシュを使用して、カテゴリとそのすべての子 (または子とそのすべての親) を直接リンクできます。

実際のカテゴリ テーブル。

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

テーブルが大きく、頻繁に更新される場合、これはおそらく改善される可能性があります。

于 2016-06-17T15:31:39.170 に答える