Root Node
1
2
3
4 5
lft rgt
1 36
2 15
3 8
4 5
6 7
9 14
10 11
12 13
16 21
17 18
19 20
22 35
23 28
24 25
26 27
29 34
30 31
32 33
6 7
8 9
10
11
12
13
14
15
16
17
18
19
20
21
22
35
23
28
24
25
26
27
29
34
30
32
31
33
36
#JAB14 - @EliAschkenasy – Integral DB Design
Root Node
1
2
3
4 5
lft rgt
1 36
2 15
3 8
4 5
6 7
9 14
10 11
12 13
16 21
17 18
19 20
22 35
23 28
24 25
26 27
29 34
30 31
32 33
6 7
8 9
10
11
12
13
14
15
16
17
18
19
20
21
22
35
23
28
24
25
26
27
29
34
30
32
31
33
36
ROOT NODE
root = lft == 1
#JAB14 - @EliAschkenasy – Integral DB Design
Root Node
1
2
3
4 5
lft rgt
1 36
2 15
3 8
4 5
6 7
9 14
10 11
12 13
16 21
17 18
19 20
22 35
23 28
24 25
26 27
29 34
30 31
32 33
6 7
8 9
10
11
12
13
14
15
16
17
18
19
20
21
22
35
23
28
24
25
26
27
29
34
30
32
31
33
36
TOTAL NODES
(rgt – lft + 1) / 2 = total
(36 – 1 + 1) / 2 = 18
#JAB14 - @EliAschkenasy – Integral DB Design
Root Node
1
2
3
4 5
lft rgt
1 36
2 15
3 8
4 5
6 7
9 14
10 11
12 13
16 21
17 18
19 20
22 35
23 28
24 25
26 27
29 34
30 31
32 33
6 7
8 9
10
11
12
13
14
15
16
17
18
19
20
21
22
35
23
28
24
25
26
27
29
34
30
32
31
33
36
IS LEAF NODE?
leaf = (rgt – lft ==1) ? true : false;
true = (5 – 4 == 1)
false = (8 – 3 == 1)
#JAB14 - @EliAschkenasy – Integral DB Design
Root Node
1
2
3
4 5
lft rgt
1 36
2 15
3 8
4 5
6 7
9 14
10 11
12 13
16 21
17 18
19 20
22 35
23 28
24 25
26 27
29 34
30 31
32 33
6 7
8 9
10
11
12
13
14
15
16
17
18
19
20
21
22
35
23
28
24
25
26
27
29
34
30
32
31
33
36
LEAF NODE OPTIMIZATIONOptimized implementation
SELECT x, x, xFROM #__WHERE lft = (rgt -1)
#JAB14 - @EliAschkenasy – Integral DB Design
Root Node
1
2
3
4 5
lft rgt
1 36
2 15
3 8
4 5
6 7
9 14
10 11
12 13
16 21
17 18
19 20
22 35
23 28
24 25
26 27
29 34
30 31
32 33
6 7
8 9
10
11
12
13
14
15
16
17
18
19
20
21
22
35
23
28
24
25
26
27
29
34
30
32
31
33
36
SUBTREE SELECTION OPTIMIZATION
SELECT c.type AS choices, b.type AS bottomFROM #__ AS c, #__ AS bWHERE c.lft BETWEEN b.lft AND b.rgt;
Anything between 22 and 35
#JAB14 - @EliAschkenasy – Integral DB Design
Root Node
1
2
3
4 5
lft rgt
1 36
2 15
3 8
4 5
6 7
9 14
10 11
12 13
16 21
17 18
19 20
22 35
23 28
24 25
26 27
29 34
30 31
32 33
6 7
8 9
10
11
12
13
14
15
16
17
18
19
20
21
22
35
23
28
24
25
26
27
29
34
30
32
31
33
36
SUBTREE SELECTION ONLY SUBTREEOptimized implementation
SELECT c.type AS choices, b.type AS bottomFROM #__ AS c, #__ AS bWHERE c.lft BETWEEN (b.lft+1) AND b.rgt;
Anyone between 23 and 35
#JAB14 - @EliAschkenasy – Integral DB Design
Root Node
1
2
3
4 5
lft rgt
1 36
2 15
3 8
4 5
6 7
9 14
10 11
12 13
16 21
17 18
19 20
22 35
23 28
24 25
26 27
29 34
30 31
32 33
6 7
8 9
10
11
12
13
14
15
16
17
18
19
20
21
22
35
23
28
24
25
26
27
29
34
30
32
31
33
36
#JAB14 - @EliAschkenasy – Integral DB Design
Root Node
1
2
3
4 5
lft rgt
1 36
2 15
3 8
4 5
6 7
9 14
10 11
12 13
16 21
17 18
19 20
22 35
23 28
24 25
26 27
29 34
30 31
32 33
6 7
8 9
10
11
12
13
14
15
16
17
18
19
20
21
22
35
23
28
24
25
26
27
29
34
30
32
31
33
36
#JAB14 - @EliAschkenasy – Integral DB Design
Root Node
1
2
3
4 5
lft rgt
1 36
2 15
3 8
4 5
6 7
9 14
10 11
12 13
16 21
17 18
19 20
22 35
23 28
24 25
26 27
29 34
30 31
32 33
6 7
8 9
10
11
12
13
14
15
16
17
18
19
20
21
22
35
23
28
24
25
26
27
29
34
30
32
31
33
36
PATH TO A NODE (breadcrumb)
SELECT aliasFROM #__WHERE lft < 4 AND rgt > 5ORDER BY lft ASC;
#JAB14 - @EliAschkenasy – Integral DB Design
Root Node
1
2
3
4 5
lft rgt
1 36
2 15
3 8
4 5
6 7
9 14
10 11
12 13
16 21
17 18
19 20
22 35
23 28
24 25
26 27
29 34
30 31
32 33
6 7
8 9
10
11
12
13
14
15
16
17
18
19
20
21
22
35
23
28
24
25
26
27
29
34
30
32
31
33
36
PATH TO A NODE
SELECT GROUP_CONCAT(alias SEPARATOR '/') as pathFROM optionsWHERE lft <= 4 AND rgt >= 5ORDER BY lft ASC;(sandal/blouse/skirt/woman)
#JAB14 - @EliAschkenasy – Integral DB Design
PATH TO A NODE
SELECT GROUP_CONCAT(alias SEPARATOR '/') as pathFROM con_menuWHERE lft <= 20 AND rgt >= 21 AND lft > 1ORDER BY lft ASC;(Messaging/Read Private Message)
#JAB14 - @EliAschkenasy – Integral DB Design
Root Node
1
2
3
4 5
lft rgt
1 36
2 15
3 8
4 5
6 7
9 14
10 11
12 13
16 21
17 18
19 20
22 35
23 28
24 25
26 27
29 34
30 31
32 33
6 7
8 9
10
11
12
13
14
15
16
17
18
19
20
21
22
35
23
28
24
25
26
27
29
34
30
32
31
33
36
#JAB14 - @EliAschkenasy – Integral DB Design
Root Node
1
2
3
4 5
lft rgt
1 36
2 15
3 8
4 5
6 7
9 14
10 11
12 13
16 21
17 18
19 20
22 35
23 28
24 25
26 27
29 34
30 31
32 33
6 7
8 9
10
11
12
13
14
15
16
17
18
19
20
21
22
35
23
28
24
25
26
27
29
34
30
32
31
33
36
LEVEL OF NODE
SELECT b.id, COUNT(a.id) AS levelFROM #__ AS a, #__ AS bWHERE b.lft BETWEEN a.lft AND a.rgtAND a.lft = 19GROUP BY b.id
#JAB14 - @EliAschkenasy – Integral DB Design
Root Node
1
2
3
4 5
lft rgt
1 36
2 15
3 8
4 5
6 7
9 14
10 11
12 13
16 21
17 18
19 20
22 35
23 28
24 25
26 27
29 34
30 31
32 33
6 7
8 9
10
11
12
13
14
15
16
17
18
19
20
21
22
35
23
28
24
25
26
27
29
34
30
32
31
33
36
#JAB14 - @EliAschkenasy – Integral DB Design
Root Node
1
2
3
4 5
lft rgt
1 36
2 15
3 8
4 5
6 7
9 14
10 11
12 13
16 21
17 18
19 20
22 35
23 28
24 25
26 27
29 34
30 31
32 33
6 7
8 9
10
11
12
13
14
15
16
17
18
19
20
21
22
35
23
28
24
25
26
27
29
34
30
32
31
33
36
PARENT ID OF A NODE
SELECT id, (SELECT idFROM #__ t2 WHERE t2.lft < t1.lft AND t2.rgt > t1.rgt ORDER BY t2.rgt-t1.rgt ASCLIMIT 1)
AS parentid FROM #__ t1 ORDER BY (rgt-lft) DESC
#JAB14 - @EliAschkenasy – Integral DB Design
Root Node
1
2
3
4 5
lft rgt
1 36
2 15
3 8
4 5
6 7
9 14
10 11
12 13
16 21
17 18
19 20
22 35
23 28
24 25
26 27
29 34
30 31
32 33
6 7
8 9
10
11
12
13
14
15
16
17
18
19
20
21
22
35
23
28
24
25
26
27
29
34
30
32
31
33
36
MAXIMUM DEPTH
SELECT MAX(level) AS heightFROM (
SELECT b.id, (COUNT(a.id) - 1) AS levelFROM #__ AS a, #__ AS bWHERE b.lft BETWEEN a.lft AND a.rgtGROUP BY b.id) AS L1
#JAB14 - @EliAschkenasy – Integral DB Design
Root Node
1
2
3
4 5
lft rgt
1 36
2 15
3 8
4 5
6 7
9 14
10 11
12 13
16 21
17 18
19 20
22 35
23 28
24 25
26 27
29 34
30 31
32 33
6 7
8 9
10
11
12
13
14
15
16
17
18
19
20
21
22
35
23
28
24
25
26
27
29
34
30
32
31
33
36
INSERT A NODE
UPDATE #__ SET rgt=rgt+2 WHERE rgt >= 25;UPDATE #__ SET lft=lft+2 WHERE lft >= 24;INSERT INTO #__ SET lft=24, rgt=25, ….;
#JAB14 - @EliAschkenasy – Integral DB Design
Root Node
1
2
3
4 5
lft rgt
1 36
2 15
3 8
4 5
6 7
9 14
10 11
12 13
16 21
17 18
19 20
22 35
23 28
24 25
26 27
29 34
30 31
32 33
6 7
8 9
10
11
12
13
14
15
16
17
18
19
20
21
22
35
23
28
24
25
26
27
29
34
30
32
31
33
36
INSERT A NODE
UPDATE #__ SET rgt=rgt+2 WHERE rgt >= 25;UPDATE #__ SET lft=lft+2 WHERE lft >= 24;INSERT INTO #__ SET lft=24, rgt=25, ….;
#JAB14 - @EliAschkenasy – Integral DB Design
Root Node
1
2
3
4 5
lft rgt
1 36
2 15
3 8
4 5
6 7
9 14
10 11
12 13
16 21
17 18
19 20
22 35
23 28
24 25
26 27
29 34
30 31
32 33
6 7
8 9
10
11
12
13
14
15
16
17
18
19
20
21
22
37
23
30
26
27
28
29
31
36
32
34
33
35
38
INSERT A NODE
UPDATE #__ SET rgt=rgt+2 WHERE rgt >= 25;UPDATE #__ SET lft=lft+2 WHERE lft >= 24;INSERT INTO #__ SET lft=24, rgt=25, ….;
24
25
#JAB14 - @EliAschkenasy – Integral DB Design
• WHO AM I ?
• NESTED SET INTRO
• NESTED SET BASIC QUERIES
• NESTED SET BASIC QUERIES OPTIMIZATION
•
•
•
#JAB14 - @EliAschkenasy – Integral DB Design
Common INSERT
INSERT INTO #__ (part_number, unit_price,eau,….)VALUES (….);
Common SELECT
SELECT unit_price FROM #__WHERE part_number = $part_number;
#JAB14 - @EliAschkenasy – Integral DB Design
Common INSERT
INSERT INTO #__ (part_number, unit_price,eau,….)VALUES (….);
Common SELECT
SELECT unit_price FROM #__WHERE part_number = $part_number;
PROBLEMS:
1. Speed2. Data accuracy (inconsistent white spacing)
#JAB14 - @EliAschkenasy – Integral DB Design
Amended INSERT
php: $concat = preg_replace('/\s+/', '', $input);
INSERT INTO #__ (part_number, unit_price, eau,part_number_concat,…)VALUES (…,$concat);
Amended SELECT
SELECT unit_price FROM #__WHERE part_number_concat = $concat;
PROBLEMS:
1. Speed2. Data accuracy (inconsistent white spacing)
#JAB14 - @EliAschkenasy – Integral DB Design
Optimized INSERT
php: $concat = preg_replace('/\s+/', '', $input);php: $crc = crc32($concat);
INSERT INTO #__ (part_number, unit_price, eau,part_number_concat,crc_partnumberconcat…)VALUES (…,$concat,$crc);
Optimized SELECT
SELECT unit_price FROM #__WHERE crc_partnumberconcat = $crcAND part_number_concat = $concat; (singularity)
PROBLEMS:
1. Speed2. Data accuracy (inconsistent white spacing)
#JAB14 - @EliAschkenasy – Integral DB Design
Optimized INSERT
php: $concat = preg_replace('/\s+/', '', $input);php: $crc = crc32($concat);
INSERT INTO #__ (part_number, unit_price, eau,part_number_concat,crc_partnumberconcat…)VALUES (…,$concat,$crc);
Optimized SELECT
SELECT unit_price FROM #__WHERE crc_partnumberconcat = $crcAND part_number_concat = $concat; (singularity)
PROBLEMS:
1. Speed2. Data accuracy (inconsistent 32bit vs 64bit)
#JAB14 - @EliAschkenasy – Integral DB Design
Optimized INSERT
php: $concat = preg_replace('/\s+/', '', $input);
INSERT INTO #__ (part_number, unit_price, eau,part_number_concat,sha_partnumberconcat…)VALUES (…,$concat,SHA1($concat));
Optimized SELECT
SELECT unit_price FROM #__WHERE sha_partnumberconcat = SHA1($concat);AND part_number_concat = $concat; (singularity)
PROBLEMS:
1. Speed2. Data accuracy (inconsistent 32bit vs 64bit)
#JAB14 - @EliAschkenasy – Integral DB Design
Optimized INSERT (* BATCH)
php: $concat = preg_replace('/\s+/', '', $input);
ALTER TABLE #__ DROP INDEX xINSERT INTO #__ (part_number, unit_price, eau,part_number_concat,sha_partnumberconcat…)VALUES (…,$concat concat,SHA1($concat));ALTER TABLE #__ ADD INDEX x (‘column’)
#JAB14 - @EliAschkenasy – Integral DB Design
Hits Counter Table
CREATE TABLE hit_counter (cnt INT UNSIGNED NOT NULL DEFAULT 0
) ENGINE=InnoDB;INSERT INTO hit_counter (cnt) VALUES (0);
UPDATE hit_counter SET cnt = cnt + 1;
SELECT cnt FROM hit_counter;
#JAB14 - @EliAschkenasy – Integral DB Design
Hits Counter Table
CREATE TABLE hit_counter (cnt INT UNSIGNED NOT NULL DEFAULT 0
) ENGINE=InnoDB;INSERT INTO hit_counter (cnt) VALUES (0);
UPDATE hit_counter SET cnt = cnt + 1;
SELECT cnt FROM hit_counter;
#JAB14 - @EliAschkenasy – Integral DB Design
Hits Counter Table (Optimized)
CREATE TABLE hit_counter (slot TINYINT UNSIGNED NOT NULL PRIMARY KEY,cnt INT UNSIGNED NOT NULL
) ENGINE=InnoDB;INSERT INTO hit_counter VALUES(0,0), (1,0), (2,0), (3,0), (4,0);-- any amount of slots you require (n-1)
UPDATE hit_counter SET cnt = cnt + 1WHERE slot = FLOOR(RAND() * 5);-- the amount of slots you assigned
SELECT SUM(cnt) FROM hit_counter;
• WHO AM I ?
• NESTED SET INTRO
• NESTED SET BASIC QUERIES
• NESTED SET BASIC QUERIES OPTIMIZATION
• STRING LOOKUP OPTIMIZATION TRICK
• INDEXING OPTIONS
•
#JAB14 - @EliAschkenasy – Integral DB Design
#JAB14 - @EliAschkenasy – Integral DB Design
SELECT * FROM Orgchart SELECT a, c FROM Orgchart SELECT a, c FROM OrgchartWHERE lft - rgt = 1; WHERE lft - rgt = 1; WHERE lft =(rgt – 1);
SELECT name FROM people mysql> ALTER TABLE people ADD KEY (idx_name(6));WHERE name = ‘Hans’;
CREATE TABLE t ( KEY(c1,c2,c3) ? KEY(c1,c3) Specificity!c1 INT,c2 INT,c3 INT,KEY(c1),KEY(c2),KEY(c3)
);
#JAB14 - @EliAschkenasy – Integral DB Design
SELECT * FROM Orgchart SELECT a, c FROM Orgchart SELECT a, c FROM OrgchartWHERE lft - rgt = 1; WHERE lft - rgt = 1; WHERE lft =(rgt – 1);
SELECT name FROM people mysql> ALTER TABLE people ADD KEY (idx_name(6));WHERE name = ‘Hans’;
CREATE TABLE t ( KEY(c1,c2,c3) ? KEY(c1,c3) Specificity!c1 INT,c2 INT,c3 INT,KEY(c1),KEY(c2),KEY(c3)
);
SELECT cc FROM payment WHERE staff_id = 2 AND customer_id = 584;KEY(staff_id,customer_id) ?SELECT SUM(staff_id = 2), SUM(customer_id = 584) FROM payment\G*************************** 1. row ***************************SUM(staff_id = 2): 7992SUM(customer_id = 584): 30ALTER TABLE payment ADD KEY(customer_id, staff_id);
#JAB14 - @EliAschkenasy – Integral DB Design
CREATE TABLE profile(id INT UNSIGNED NOT NULL AUTO_INCREMENT,sex CHAR(1) NOT NULL,age TINYINT NOT NULL,country VARCHAR(255) NOT NULL,region VARCHAR(255) NOT NULL DEFAULT ‘’,city VARCHAR(255) NOT NULL DEFAULT ‘’,color_hair VARCHAR(255) NOT NULL DEFAULT ‘’,color_eyes VARCHAR(255) NOT NULL DEFAULT ‘’,name
……
rating TINYINT NOT NULL DEFAULT 1,PRIMARY KEY(id)
);
#JAB14 - @EliAschkenasy – Integral DB Design
CREATE TABLE profile(id INT UNSIGNED NOT NULL AUTO_INCREMENT,sex CHAR(1) NOT NULL,age TINYINT NOT NULL,country VARCHAR(255) NOT NULL,region VARCHAR(255) NOT NULL DEFAULT ‘’,city VARCHAR(255) NOT NULL DEFAULT ‘’,color_hair VARCHAR(255) NOT NULL DEFAULT ‘’,color_eyes VARCHAR(255) NOT NULL DEFAULT ‘’,name
……
rating TINYINT NOT NULL DEFAULT 1,PRIMARY KEY(id)
);
WHERE age BETWEEN 18 AND 25ORDER BY rating ASCMySQL can’t use added index if primary index uses range criterion
KEY(sex, country)NO Selectivity!!!
KEY(sex, country)Assumption: all searches will include sex and most will include country
Trick: AND sex IN('m', 'f')
#JAB14 - @EliAschkenasy – Integral DB Design
CREATE TABLE profile(id INT UNSIGNED NOT NULL AUTO_INCREMENT,sex CHAR(1) NOT NULL,age TINYINT NOT NULL,country VARCHAR(255) NOT NULL,region VARCHAR(255) NOT NULL DEFAULT ‘’,city VARCHAR(255) NOT NULL DEFAULT ‘’,color_hair VARCHAR(255) NOT NULL DEFAULT ‘’,color_eyes VARCHAR(255) NOT NULL DEFAULT ‘’,name
……
rating TINYINT NOT NULL DEFAULT 1,PRIMARY KEY(id)
);
(sex, country, age)(sex, country, region, age)(sex, country, region, city, age)
Using the IN() trick, we can implement just the(sex, country, region, city, age) index.
Why is age at end?Remember our range problem?MySQL uses indexes from left to right until the first range queryTrick: Convert WHERE age BETWEEN 18 and 25 toWHERE age IN(18,19,20,21,22,23,24,25)