å ¥ãåéåã¢ãã«ã§åä¾ãã¼ã¿ãåå¾ããéã®ããã©ã¼ãã³ã¹
RDBã§é層æ§é ï¼å¹³ããè¨ãã¨è¡å士ã®è¦ªåé¢ä¿ï¼ã表ç¾ããããã«æ§ã
ãªãã¼ã¿ã¢ãã«ãåå¨ãã¾ããæããã使ç¨ããã¦ããã¨æãããã®ã¯ãé£æ¥ãªã¹ãã¢ãã«ãï¼åã¨ãªãè¡ã®ä¸ã§ã親ã¨ãªãè¡ã®primary keyãunique idãæã£ã¦ãããã¨ã§è¦ªåé¢ä¿ã表ç¾ãããã¼ã¿ã¢ãã«ï¼ã§ãã
ãã ãããã®ã¢ãã«ã§ã¯ãNé層以å
ã®è¦ªãåå¾ããããNé層以å
ã®åãåå¾ããããã¨ã¯å®¹æã§ãããé層æ°ã«ä¾åããå帰çã«ãå
¨ã¦ã®ç¥å
ãåå¾ããããå
¨ã¦ã®åå«ãåå¾ããããã¨ãè¦æã§ãã*1
ãã®ãããªæ¬ ç¹ãæããªããã¼ã¿ã¢ãã«ã¨ãã¦ããæ°å¹´ã§æ³¨ç®ãéãã¦ããã¢ãã«ã¨ãã¦ãå ¥ãåéåã¢ãã«ããããã¾ãã
SQLで木と階層構造のデータを扱う(1)―― 入れ子集合モデル
å¾æ¥ã®ãªã¬ã¼ã·ã§ãã«ã»ãã¼ã¿ãã¼ã¹ã§é層ãã¼ã¿ãæ±ãã¢ãã«ã«ã¯ããé£æ¥ãªã¹ãã¢ãã«ãããçµè·¯åæã¢ãã«ããªã©ãç¥ããã¦ãã¾ããããããããã¼ã¿æ§é ã®ã¤ã¡ã¼ã¸ãã¤ãã¿ã¥ãããSQL ãè¤éã§æ¡å¼µæ§ã«ä¹ããã¨ããæ¬ ç¹ãæã£ã¦ããã®ã«å¯¾ããç´è¦³çã«ç解ãããããSQL ã大å¤ç°¡æ½ã«æ¸ããã¨ããã¡ãªãããããã¾ãã
ä¸è¦ããã³ãããããªãå ¥ãåéåã¢ãã«ãã§ãããMySQLä¸ã§ä½¿ç¨ããå ´åã«ããã©ã¼ãã³ã¹ä¸åé¡ã«ãªã£ããã¨ãæã£ãã®ã§ãã¡ã¢ã¨ãã¦æ®ãã¦ããã¾ãã
ãµã³ãã«ãã¼ã¿
å ãã¯ãµã³ãã«ãã¼ã¿ã¨ãã¦ããããªæãã§é½éåºçãå°åãã¨ã«åãã¦ã親åé¢ä¿ãå ¥ãåéåã¢ãã«ã§è¡¨ãããã¼ã¿ãä½ãã¾ããã¾ãã件æ°ã稼ãããã«ããã¼ãã¼ã¿ãå ¥ãã¦ç·è¨1ä¸è¡ã®ãã¼ã¿ã¨ãã¾ãã
ãã¼ãã«
CREATE TABLE `region` ( `id` INT NOT NULL AUTO_INCREMENT, `name` VARCHAR(50) NULL DEFAULT NULL, `lft` INT NULL DEFAULT NULL, `rgt` INT NULL DEFAULT NULL, PRIMARY KEY (`id`), INDEX `lft` (`lft`), INDEX `rgt` (`rgt`) )
ãã¼ã¿ã
id | name | lft | rgt |
---|---|---|---|
1 | æ¥æ¬ | 1 | 113 |
2 | åæµ·éå°æ¹ | 2 | 5 |
3 | æ±åå°æ¹ | 6 | 19 |
4 | é¢æ±å°æ¹ | 20 | 35 |
5 | ä¸é¨å°æ¹ | 36 | 55 |
6 | è¿ç¿å°æ¹ | 56 | 71 |
7 | ä¸å½å°æ¹ | 72 | 83 |
8 | åå½å°æ¹ | 84 | 92 |
9 | ä¹å·å°æ¹ | 93 | 108 |
10 | æ²ç¸å°æ¹ | 109 | 112 |
11 | åæµ·é | 3 | 4 |
12 | é森ç | 7 | 8 |
13 | 岩æç | 9 | 10 |
14 | å®®åç | 11 | 12 |
15 | ç§ç°ç | 13 | 14 |
16 | 山形ç | 15 | 16 |
17 | ç¦å³¶ç | 17 | 18 |
18 | è¨åç | 21 | 22 |
19 | æ æ¨ç | 23 | 24 |
20 | 群馬ç | 25 | 26 |
21 | å¼çç | 27 | 28 |
22 | åèç | 29 | 30 |
23 | æ±äº¬é½ | 31 | 32 |
24 | ç¥å¥å·ç | 33 | 34 |
25 | æ°æ½ç | 37 | 38 |
26 | å¯å±±ç | 39 | 40 |
27 | ç³å·ç | 41 | 42 |
28 | ç¦äºç | 43 | 44 |
29 | 山梨ç | 45 | 46 |
30 | é·éç | 47 | 48 |
31 | å²éç | 49 | 50 |
32 | é岡ç | 51 | 52 |
33 | æç¥ç | 53 | 54 |
34 | ä¸éç | 57 | 58 |
35 | æ»è³ç | 59 | 60 |
36 | 京é½åº | 61 | 62 |
37 | 大éªåº | 63 | 64 |
38 | å µåº«ç | 65 | 66 |
39 | å¥è¯ç | 67 | 68 |
40 | åæå±±ç | 69 | 70 |
41 | é³¥åç | 73 | 74 |
42 | å³¶æ ¹ç | 75 | 76 |
43 | 岡山ç | 77 | 78 |
44 | åºå³¶ç | 79 | 80 |
45 | å±±å£ç | 81 | 82 |
46 | 徳島ç | 85 | 86 |
47 | é¦å·ç | 87 | 88 |
48 | æåªç | 88 | 89 |
49 | é«ç¥ç | 90 | 91 |
50 | ç¦å²¡ç | 94 | 95 |
51 | ä½è³ç | 96 | 97 |
52 | é·å´ç | 98 | 99 |
53 | çæ¬ç | 100 | 101 |
54 | 大åç | 102 | 103 |
55 | å®®å´ç | 104 | 105 |
56 | 鹿å 島ç | 106 | 107 |
57 | æ²ç¸ç | 110 | 111 |
58 | null | 114 | 115 |
59 | null | 116 | 117 |
.. | .. | .. | .. |
10000 | null | 19998 | 19999 |
ï¼ã«ã¼ãã¨ãªããã¼ã¿ãæ¥æ¬ã+ 9å°å + 47é½éåºçã57件ã+ããã¼ãã¼ã¿ãå«ãã¦1ä¸ä»¶ã¨ãªãã¾ãã
å°ååºåã«ã¤ãã¦ã¯Wikipediaã都道府県/地域順の一覧ããåèã¨ãã¾ãããã¾ãããã¼ã¿ã®insertå¾ã«çµ±è¨æ
å ±ãæ´æ°ããããanalyze tableãå®è¡ãã¦ãã¾ããï¼
åå«ï¼åä¾ã»å«ã»ã²å«....ï¼ãåå¾ããã¯ã¨ãª
ä¾ãã°ãnameã«ã©ã ã®å¤ããæ¥æ¬ãï¼id=1, lft=1, rgt=113ï¼ã®åå«ã¨ãªããã¼ã¿ã¯ä¸è¨ã®ããã«çµãè¾¼ãã¾ãã
SELECT children.* FROM region AS parents LEFT OUTER JOIN region children ON children.lft > parents.lft AND children.lft < parents.rgt WHERE parents.id=1
ããã§ãå°åã¨é½éåºçã®ãã¼ã¿56件ãå
¨ã¦åå¾ãããã¨ãå¯è½ã§ãã
ãããããã®æ¡ä»¶ã§ã¯é½éåºçã®ãã¼ã¿ãé¤ãã¦å°åãã¼ã¿ã®ã¿ãåå¾ãããã¨ãã§ãã¾ããã
ç´æ¥ã®åä¾ãåå¾ããã¯ã¨ãªãé ã
ããã§ããæ¥æ¬ãã®ç´æ¥ã®åä¾ã ããåå¾ãããããここã§æãããã¦ããä¸è¨ã®ãããªã¯ã¨ãªã使ç¨ãã¾ãã
ã»ã¯ã¨ãª1
SELECT children.* FROM region as parents LEFT OUTER JOIN region children ON parents.lft = (SELECT MAX(lft) FROM region WHERE children.lft > lft AND children.lft < rgt) WHERE parents.id=1
ãã®ã¯ã¨ãªãå®è¡ããã¨ããæ¥æ¬ãã®ç´æ¥ã®åã©ãã¨ãªãå°ååºåã®ãã¼ã¿ã ããåå¾ã§ãã¾ãã
å®è¡çµæ
id | name | lft | rgt |
---|---|---|---|
2 | åæµ·éå°æ¹ | 2 | 5 |
3 | æ±åå°æ¹ | 6 | 19 |
4 | é¢æ±å°æ¹ | 20 | 35 |
5 | ä¸é¨å°æ¹ | 36 | 55 |
6 | è¿ç¿å°æ¹ | 56 | 71 |
7 | ä¸å½å°æ¹ | 72 | 83 |
8 | åå½å°æ¹ | 84 | 92 |
9 | ä¹å·å°æ¹ | 93 | 108 |
10 | æ²ç¸å°æ¹ | 109 | 112 |
ãããããã®ã¯ã¨ãªã¯é常ã«é
ãã§ãã
ã©ãã ãé
ããã¨ããã¨ããã¼ã«ã«ç°å¢ã§æ¸¬å®ããã¯ã¨ãªå®è¡æéã¯ããããã*2ã
å®è¡æé(sec.) | |
---|---|
1åç® | 37.596 |
2åç® | 37.768 |
3åç® | 37.783 |
ç·ä»¶æ°1ä¸ä»¶ã®ãã¼ãã«ã«ãã¦ã¯æ¿ããé ããã¨ãåããã¾ãã
EXPLAINçµæ
ãã®ã¯ã¨ãªã®EXPLAINãåã£ã¦ã¿ã¾ãããã
EXPLAINçµæã¯ä»¥ä¸ã®ã¨ããã§ãã
id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
---|---|---|---|---|---|---|---|---|---|
1 | PRIMARY | parents | const | PRIMARY | PRIMARY | 4 | const | 1 | NULL |
1 | PRIMARY | children | ALL | NULL | NULL | NULL | NULL | 10327 | Using where |
2 | DEPENDENT SUBQUERY | region | ALL | lft,rgt | NULL | NULL | NULL | 10327 | Range checked for each record (index map: 0x6) |
ä¸è¡ç®ã¯ã"parents.id=1"ã®ã¨ãã§ãããprimary keyã¸ã®æ¤ç´¢ã ããããã«ã¤ãã¦ã¯ã¾ãåé¡ç¡ãã
äºè¡ç®ã«ã¤ãã¦ã¯èªå·±çµåæ¡ä»¶ï¼"LEFT OUTER JOIN region children ON ï½"ï¼ã«é¢ããã¯ã¨ãªã¨æããã¾ããããã¼ãã«ã¹ãã£ã³ãè¡ããã¦ã¾ããrgt, lftã«ã©ã ã¸ã®ã¤ã³ããã¯ã¹ã¯ä½¿ç¨ããã¦ãã¾ããã"analyze table"ãå®è¡ããããrgt,lftã«ã©ã ã«è¤åã¤ã³ããã¯ã¹ã追å ããããã¾ããããã©ããã£ã¦ããã¼ãã«ã¹ãã£ã³ãè¡ããã¾ãã
ä¸è¡ç®ã¯ç¸é¢ãµãã¯ã¨ãªï¼(SELECT MAX(lft) FROM regionï¼ï½ï¼ä»¥ä¸ã®é¨åã§ãããã¡ããrgt,lftã«ã©ã ã¸ã®ã¤ã³ããã¯ã¹ã使ã£ã¦ãããããã¼ãã«ã¹ãã£ã³ã§ããã¾ããå®éã«ã¯10327件ã§ã¯ãªãããå¤å´é¨åã®åå¾ä»¶æ°ï¼äºè¡ç®ã®æ¤ç´¢çµæï¼ Ã 10327件ãã®è©ä¾¡ãè¡ããã¾ãã
ã¤ã¾ã
- ãã¼ãã«ã¹ãã£ã³ã1åå®è¡ãããï¼Explainçµæã®äºè¡ç®ãä¸è¡ç®ï¼
- ä¸åç®ã®ãã¼ãã«ã¹ãã£ã³éè¨çµæã«å¯¾ãã¦ãããã«éè¨çµæ à 10327件åã®ãã¼ãã«ã¹ãã£ã³ãå®è¡ãããï¼Explainçµæã®ä¸è¡ç®ï¼
ã¨ããããªãããã¼ãªå¦çã§ãã
ãã®åé¡ã«ã¤ãã¦ã¯ãここã§æãããã¦ããããä¸ã¤ã®ã¿ã¤ãã®ã¯ã¨ãªã使ç¨ãããã¨ã§è§£æ±ºãã¾ãã
ã»ã¯ã¨ãª2
SELECT children.* FROM region AS parents LEFT OUTER JOIN region children ON children.lft > parents.lft AND children.lft < parents.rgt WHERE parents.id=1 AND NOT EXISTS ( SELECT id FROM region AS mid_parents WHERE mid_parents.lft BETWEEN parents.lft AND parents.rgt AND children.lft BETWEEN mid_parents.lft AND mid_parents.rgt AND mid_parents.id NOT IN (children.id, parents.id))
ãã¼ã«ã«ç°å¢ã§æ¸¬å®ããã¯ã¨ãªå®è¡æéã¯ããããã*3ã
å®è¡æé(sec.) | |
---|---|
1åç® | 0.000 |
2åç® | 0.000 |
3åç® | 0.000 |
ããªç§ä»¥ä¸ã§ãã
EXPLAINçµæ
id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
---|---|---|---|---|---|---|---|---|---|
1 | PRIMARY | parents | const | PRIMARY | PRIMARY | 4 | const | 1 | NULL |
1 | PRIMARY | children | ALL | lft | NULL | NULL | NULL | 55 | Using where |
2 | DEPENDENT SUBQUERY | mid_parents | range | lft,rgt | lft | 5 | NULL | 57 | Range checked for each record (index map: 0x6) |
äºè¡ç®ãä¸è¡ç®ã®ã¹ãããã§ãã¤ã³ããã¯ã¹ã使ç¨ãããããã«éè¨å¯¾è±¡ä»¶æ°ã大å¹
ã«çµããã¾ãã¦ãããã¨ã確èªã§ãã¾ãã
åèæ å ±
EXPLAINçµæã®èªã¿æ¹ã«ã¤ãã¦ã¯ä»¥ä¸ã®ãã¼ã¸ãåèã«ãã¾ããã
MySQL :: MySQL 5.1 リファレンスマニュアル :: 6.2.1 EXPLAINを使用して、クエリを最適化する
æ¼¢(ãªãã³)ã®ã³ã³ãã¥ã¼ã¿é: MySQLã®EXPLAINãå¾¹åºè§£èª¬!!
PostgreSQLã§ã¯ã©ããªã®ï¼
ã¯ã¨ãª1ã®EXPLAIN ANALYZEå®è¡çµæ
Hash Left Join (cost=280.28..498.72 rows=1 width=22) (actual time=11206.949..11207.157 rows=9 loops=1) Hash Cond: (parents.lft = (SubPlan 2)) -> Index Scan using id on region parents (cost=0.29..8.30 rows=1 width=4) (actual time=0.016..0.018 rows=1 loops=1) Index Cond: (id = 1) -> Hash (cost=155.00..155.00 rows=10000 width=22) (actual time=11206.859..11206.859 rows=56 loops=1) Buckets: 1024 Batches: 1 Memory Usage: 4kB -> Seq Scan on region children (cost=0.00..155.00 rows=10000 width=22) (actual time=0.005..10.161 rows=10000 loops=1) SubPlan 2 -> Result (cost=0.41..0.42 rows=1 width=0) (actual time=1.112..1.113 rows=1 loops=10009) InitPlan 1 (returns $1) -> Limit (cost=0.29..0.41 rows=1 width=4) (actual time=1.108..1.108 rows=0 loops=10009) -> Index Scan Backward using lft on region (cost=0.29..137.28 rows=1111 width=4) (actual time=1.104..1.104 rows=0 loops=10009) Index Cond: ((lft IS NOT NULL) AND (children.lft > lft)) Filter: (children.lft < rgt) Rows Removed by Filter: 4995
ã¯ã¨ãª2ã®EXPLAIN ANALYZEå®è¡çµæ
Nested Loop Anti Join (cost=0.86..13103.49 rows=1111 width=22) (actual time=0.045..0.976 rows=9 loops=1) -> Nested Loop Left Join (cost=0.57..67.92 rows=1111 width=34) (actual time=0.030..0.204 rows=56 loops=1) -> Index Scan using id on region parents (cost=0.29..8.30 rows=1 width=12) (actual time=0.016..0.017 rows=1 loops=1) Index Cond: (id = 1) -> Index Scan using lft on region children (cost=0.29..48.51 rows=1111 width=22) (actual time=0.009..0.071 rows=56 loops=1) Index Cond: ((lft > parents.lft) AND (lft < parents.rgt)) -> Index Scan using lft on region mid_parents (cost=0.29..10.49 rows=123 width=12) (actual time=0.009..0.009 rows=1 loops=56) Index Cond: ((lft >= parents.lft) AND (lft <= parents.rgt) AND (children.lft >= lft)) Filter: ((children.lft <= rgt) AND (id <> ALL (ARRAY[children.id, parents.id]))) Rows Removed by Filter: 26
ã»ã»ã»ã»PostgreSQLã®EXPLAINã¯ãã¾ãã¡ããåãããªãã
ã¯ã¨ãª1å®è¡æéï¼secï¼ | ã¯ã¨ãª2å®è¡æéï¼secï¼ | |
---|---|---|
1åç® | 11.207 | 0.001 |
2åç® | 11.137 | 0.001 |
3åç® | 11.301 | 0.001 |
MySQLã»ã©ã§ã¯ãªãã«ããããã¯ãã¯ã¨ãª1ã¯ã¯ã¨ãª2ã¨æ¯è¼ããã¨æ¡éãã«é ããã¨ãåããã¾ãã
*1:åå¾ããé層ã«å¿ãã¦Nåã®JOINãä½ãå¿ è¦ãæããããå帰ã¯ã¨ãªæ§æï¼http://ja.wikipedia.org/wiki/å帰ã¯ã¨ãªï¼ããµãã¼ããã¦ããRDBMSã§ããã°ãã®åé¡ã¯ç¡ããªãã¯ãã
*2:HeidiSQLä¸ã§åå¾ããå¤ãã¾ããåã¯ã¨ãªå®è¡åã«ã¯ã¨ãªãã£ãã·ã¥ã®åé¤ãè¡ã£ã¦ããã
*3:HeidiSQLä¸ã§åå¾ããå¤ãã¾ããåã¯ã¨ãªå®è¡åã«ã¯ã¨ãªãã£ãã·ã¥ã®åé¤ãè¡ã£ã¦ããã