Your SlideShare is downloading. ×
0
Models for
Hierarchical Data
with SQL and PHP
Bill Karwin
PHP TEK-X • Chicago • 2010/05/20
Me
• 20+ years experience
  •   Application/SDK developer
  •   Support, Training, Proj Mgmt
  •   C, Java, Perl, PHP

• S...
Problem


Store & query hierarchical data
        Categories/subcategories
            Bill of materials
         Threaded...
Example: Bug Report Comments
                                (1) Fran:
                             What’s the cause
     ...
Solutions

 Adjacency list
Path enumeration
  Nested sets
 Closure table
Adjacency List
Adjacency List
• Naive solution nearly everyone uses
• Each entry knows its immediate parent
  comment_id parent_id author...
Insert a New Node
INSERT INTO Comments (parent_id, author, comment)

 VALUES (5, ‘Fran’, ‘I agree!’);
                    ...
Move a Node or Subtree
UPDATE Comments SET parent_id = 3
WHERE comment_id = 6;
                                           ...
Move a Node or Subtree
UPDATE Comments SET parent_id = 3
WHERE comment_id = 6;
                                           ...
Query Immediate Child/Parent

• Query a node’s 
children:
     SELECT * FROM Comments c1
     LEFT JOIN Comments c2
      ...
Can’t Handle Deep Trees

SELECT * FROM Comments c1
LEFT JOIN Comments c2 ON (c2.parent_id = c1.comment_id)
LEFT JOIN Comme...
SQL-99 recursive syntax
WITH [RECURSIVE] CommentTree

 (comment_id, bug_id, parent_id, author, comment, depth)
AS (

 SELE...
Path Enumeration
Path Enumeration
• Store chain of ancestors in each node
                                          good for
              ...
Query Ancestors and Subtrees

• Query ancestors of comment #7:
    SELECT * FROM Comments
    WHERE ‘1/4/6/7/’ LIKE path |...
Add a New Child of #7

INSERT INTO Comments (author, comment)
VALUES (‘Ollie’, ‘Good job!’);
SELECT path FROM Comments
WHE...
Nested Sets
Nested Sets
• Each comment encodes its descendants
  using two numbers:
  •   A comment’s left number is less than all
   ...
What Does This Look Like?

                                          (1) Fran:
                                       What...
What Does This Look Like?

comment_id   nsleft   nsright   author            comment
    1          1        14       Fran...
Query Ancestors of #7

                                        (1) Fran:
                                                 ...
Query Ancestors of #7


SELECT * FROM Comments child
JOIN Comments ancestor
 ON (child.nsleft BETWEEN ancestor.nsleft

 
 ...
Query Subtree Under #4

                                         (1) Fran:                                    parent
     ...
Query Subtree Under #4


SELECT * FROM Comments parent
JOIN Comments descendant
 ON (descendant.nsleft BETWEEN parent.nsle...
Insert New Child of #5

                                        (1) Fran:
                                     What’s the ...
Insert New Child of #5
UPDATE Comments
SET
 nsleft = CASE WHEN nsleft >= 8 THEN nsleft+2

 ELSE nsleft END,

 nsright = ns...
Query Immediate Parent of #6

                                           (1) Fran:
                                       ...
Query Immediate Parent of #6


• Parent of #6 is an ancestor who has no
  descendant who is also an ancestor of #6.
  SELE...
Closure Table
Closure Table



•   Store every path...
    • ...from parent node to each descendant
    • ...from each ancestor to child...
Closure Table

CREATE TABLE TreePaths (

 ancestor

 INT NOT NULL,

 descendant
 INT NOT NULL,

 PRIMARY KEY (ancestor, de...
Closure Table illustration

                                    (1) Fran:
                                 What’s the caus...
What Does This Look Like?
                                                      ancestor descendant
                      ...
Query Descendants of #4


SELECT c.* FROM Comments c
JOIN TreePaths t
 ON (c.comment_id = t.descendant)
WHERE t.ancestor =...
Paths Starting from #4

                                   (1) Fran:
                                What’s the cause
    ...
Query Ancestors of #6


SELECT c.* FROM Comments c
JOIN TreePaths t
 ON (c.comment_id = t.ancestor)
WHERE t.descendant = 6;
Paths Terminating at #6

                                   (1) Fran:
                                What’s the cause
   ...
Insert New Child of #5

INSERT INTO Comments

 VALUES (8, ‘Fran’, ‘I agree!’);
INSERT INTO TreePaths (ancestor, descendant...
Copy Paths from Parent

                                    (1) Fran:
                                 What’s the cause
  ...
Delete Child #7



DELETE FROM TreePaths
WHERE descendant = 7;
Delete Paths Terminating at #7

                                       (1) Fran:
                                    What’...
Delete Paths Terminating at #7

                                       (1) Fran:
                                    What’...
Delete Subtree Under #4


DELETE FROM TreePaths
WHERE descendant IN

 (SELECT descendant FROM TreePaths

 WHERE ancestor =...
Delete Paths Terminating at
  Any Node Under #4
                                     (1) Fran:
                           ...
Delete Paths Terminating at
  Any Node Under #4
                                     (1) Fran:
                           ...
Path Length
                                      ancestor descendant length


• Add a length column                    1
...
Choosing the Right Design

                       Query    Query    Delete   Insert    Move     Referential
  Design      ...
PHP Tree Class
Hierarchical Test Data


• Integrated Taxonomic Information System
 •   http://ITIS.GOV/

 •   Free authoritative taxonomi...
California Poppy
Kingdom:
Plantae
Division:
 Tracheobionta
Class:
 
 Magnoliophyta
Order:
 Magnoliopsida
unranked:
Magnoli...
California Poppy: ITIS Entry

          SELECT * FROM Hierarchy WHERE
            hierarchy_string LIKE ‘%-18956’;

      ...
Hierarchical Data Classes


abstract class ZendX_Db_Table_TreeTable

 extends Zend_Db_Table_Abstract
{

 public function f...
Hierarchical Data Classes
class ZendX_Db_Table_Row_TreeRow

 extends Zend_Db_Table_Row_Abstract
{

 public function addChi...
Using TreeTable


class ItisTable extends ZendX_Db_Table_TreeTable
{

 protected $_name = “longnames”;

 protected $_closu...
Breadcrumbs
$breadcrumbs = $itis->fetchBreadcrumbs(18956);
foreach ($breadcrumbs as $crumb) {

 print $crumb->completename...
Breadcrumbs SQL


SELECT a.* FROM longnames AS a
INNER JOIN treepaths AS c ON a.tsn = c.a
WHERE (c.d = 18956)
ORDER BY c.l...
How Does it Perform?
     • Query profile = 0.0006 sec
     • MySQL EXPLAIN:
table type     key     ref    rows            ...
Dump Tree
$tree = $itis->fetchTreeByRoot(18880); // Papaveraceae
print_tree($tree);


function print_tree($tree, $prefix = ...
Dump Tree Result
Papaveraceae                   Romneya
  Platystigma                    Romneya coulteri
    Platystigma ...
Dump Tree SQL
                                         expanding
                                       specific nodes
SELE...
How Does it Perform?
     • Query profile = 0.30 sec
     • MySQL EXPLAIN:
table type      key       ref        rows       ...
Demo Time!
What’s Next for TreeTable?


• http://framework.zend.com/svn/framework/
  extras/incubator
• Refactor to support multiple ...
SQL Antipatterns


• Shipping in July from
   Pragmatic Bookshelf
• Download the Beta
   e-book now


http://www.pragprog....
Copyright 2008-2010 Bill Karwin
        www.slideshare.net/billkarwin
              Released under a Creative Commons 3.0 ...
Models for hierarchical data
Models for hierarchical data
Models for hierarchical data
Models for hierarchical data
Models for hierarchical data
Models for hierarchical data
Models for hierarchical data
Models for hierarchical data
Models for hierarchical data
Models for hierarchical data
Models for hierarchical data
Models for hierarchical data
Models for hierarchical data
Models for hierarchical data
Models for hierarchical data
Models for hierarchical data
Models for hierarchical data
Models for hierarchical data
Models for hierarchical data
Models for hierarchical data
Models for hierarchical data
Models for hierarchical data
Models for hierarchical data
Models for hierarchical data
Models for hierarchical data
Upcoming SlideShare
Loading in...5
×

Models for hierarchical data

149,205

Published on

Tree-like data relationships are common, but working with trees in SQL usually requires awkward recursive queries. This talk describes alternative solutions in SQL, including:

- Adjacency List
- Path Enumeration
- Nested Sets
- Closure Table

Code examples will show using these designs in PHP, and offer guidelines for choosing one design over another.

Published in: Technology, Business
18 Comments
199 Likes
Statistics
Notes
No Downloads
Views
Total Views
149,205
On Slideshare
0
From Embeds
0
Number of Embeds
30
Actions
Shares
0
Downloads
2,618
Comments
18
Likes
199
Embeds 0
No embeds

No notes for slide

Transcript of "Models for hierarchical data"

  1. 1. Models for Hierarchical Data with SQL and PHP Bill Karwin PHP TEK-X • Chicago • 2010/05/20
  2. 2. Me • 20+ years experience • Application/SDK developer • Support, Training, Proj Mgmt • C, Java, Perl, PHP • SQL maven • Zend Framework 1.0 project manager • Author of new book SQL Antipatterns
  3. 3. Problem Store & query hierarchical data Categories/subcategories Bill of materials Threaded discussions
  4. 4. Example: Bug Report Comments (1) Fran: What’s the cause of this bug? (2) Ollie: (4) Kukla: I think it’s a null We need to pointer. check valid input. (3) Fran: (6) Fran: (5) Ollie: No, I checked for Yes, please add a Yes, that’s a bug. that. check. (7) Kukla: That fixed it.
  5. 5. Solutions Adjacency list Path enumeration Nested sets Closure table
  6. 6. Adjacency List
  7. 7. Adjacency List • Naive solution nearly everyone uses • Each entry knows its immediate parent comment_id parent_id author comment 1 NULL Fran What’s the cause of this bug? 2 1 Ollie I think it’s a null pointer. 3 2 Fran No, I checked for that. 4 1 Kukla We need to check valid input. 5 4 Ollie Yes, that’s a bug. 6 4 Fran Yes, please add a check 7 6 Kukla That fixed it.
  8. 8. Insert a New Node INSERT INTO Comments (parent_id, author, comment) VALUES (5, ‘Fran’, ‘I agree!’); (1) Fran: What’s the cause of this bug? (2) Ollie: (4) Kukla: I think it’s a null We need to check pointer. valid input. (6) Fran: (3) Fran: (5) Ollie: Yes, please add a No, I checked for that. Yes, that’s a bug. check. (8) Fran: (7) Kukla: I agree! That fixed it.
  9. 9. Move a Node or Subtree UPDATE Comments SET parent_id = 3 WHERE comment_id = 6; (1) Fran: What’s the cause of this bug? (2) Ollie: (4) Kukla: I think it’s a null We need to check pointer. valid input. (6) Fran: (3) Fran: (5) Ollie: Yes, please add a No, I checked for that. Yes, that’s a bug. check. (7) Kukla: That fixed it.
  10. 10. Move a Node or Subtree UPDATE Comments SET parent_id = 3 WHERE comment_id = 6; (1) Fran: What’s the cause of this bug? (2) Ollie: (4) Kukla: I think it’s a null We need to check pointer. valid input. (3) Fran: (5) Ollie: No, I checked for that. Yes, that’s a bug. (6) Fran: Yes, please add a check. (7) Kukla: That fixed it.
  11. 11. Query Immediate Child/Parent • Query a node’s children: SELECT * FROM Comments c1 LEFT JOIN Comments c2 ON (c2.parent_id = c1.comment_id); • Query a node’s parent: SELECT * FROM Comments c1 JOIN Comments c2 ON (c1.parent_id = c2.comment_id);
  12. 12. Can’t Handle Deep Trees SELECT * FROM Comments c1 LEFT JOIN Comments c2 ON (c2.parent_id = c1.comment_id) LEFT JOIN Comments c3 ON (c3.parent_id = c2.comment_id) LEFT JOIN Comments c4 ON (c4.parent_id = c3.comment_id) LEFT JOIN Comments c5 ON (c5.parent_id = c4.comment_id) LEFT JOIN Comments c6 ON (c6.parent_id = c5.comment_id) LEFT JOIN Comments c7 ON (c7.parent_id = c6.comment_id) LEFT JOIN Comments c8 ON (c8.parent_id = c7.comment_id) LEFT JOIN Comments c9 ON (c9.parent_id = c8.comment_id) LEFT JOIN Comments c10 ON (c10.parent_id = c9.comment_id) ... it still doesn’t support unlimited depth!
  13. 13. SQL-99 recursive syntax WITH [RECURSIVE] CommentTree (comment_id, bug_id, parent_id, author, comment, depth) AS ( SELECT *, 0 AS depth FROM Comments WHERE parent_id IS NULL UNION ALL SELECT c.*, ct.depth+1 AS depth FROM CommentTree ct JOIN Comments c ON (ct.comment_id = c.parent_id) ) SELECT * FROM CommentTree WHERE bug_id = 1234; PostgreSQL, ✓ Oracle 11g, IBM DB2, Microsoft SQL Server ✗ MySQL, SQLite, etc.
  14. 14. Path Enumeration
  15. 15. Path Enumeration • Store chain of ancestors in each node good for breadcrumbs comment_id path author comment 1 1/ Fran What’s the cause of this bug? 2 1/2/ Ollie I think it’s a null pointer. 3 1/2/3/ Fran No, I checked for that. 4 1/4/ Kukla We need to check valid input. 5 1/4/5/ Ollie Yes, that’s a bug. 6 1/4/6/ Fran Yes, please add a check 7 1/4/6/7/ Kukla That fixed it.
  16. 16. Query Ancestors and Subtrees • Query ancestors of comment #7: SELECT * FROM Comments WHERE ‘1/4/6/7/’ LIKE path || ‘%’; • Query descendants of comment #4: SELECT * FROM Comments WHERE path LIKE ‘1/4/%’;
  17. 17. Add a New Child of #7 INSERT INTO Comments (author, comment) VALUES (‘Ollie’, ‘Good job!’); SELECT path FROM Comments WHERE comment_id = 7; UPDATE Comments SET path = $parent_path || LAST_INSERT_ID() || ‘/’ WHERE comment_id = LAST_INSERT_ID();
  18. 18. Nested Sets
  19. 19. Nested Sets • Each comment encodes its descendants using two numbers: • A comment’s left number is less than all numbers used by the comment’s descendants. • A comment’s right number is greater than all numbers used by the comment’s descendants. • A comment’s numbers are between all numbers used by the comment’s ancestors.
  20. 20. What Does This Look Like? (1) Fran: What’s the cause of this bug? 1 14 (2) Ollie: (4) Kukla: I think it’s a null We need to check pointer. valid input. 2 5 6 13 (3) Fran: (6) Fran: (5) Ollie: No, I checked for Yes, please add a Yes, that’s a bug. that. check. 3 4 7 8 9 12 (7) Kukla: That fixed it. 10 11
  21. 21. What Does This Look Like? comment_id nsleft nsright author comment 1 1 14 Fran What’s the cause of this bug? 2 2 5 Ollie I think it’s a null pointer. 3 3 4 Fran No, I checked for that. 4 6 13 Kukla We need to check valid input. 5 7 8 Ollie Yes, that’s a bug. 6 9 12 Fran Yes, please add a check 7 10 11 Kukla That fixed it. these are not foreign keys
  22. 22. Query Ancestors of #7 (1) Fran: ancestors What’s the cause of this bug? 1 14 (2) Ollie: (4) Kukla: I think it’s a null We need to check pointer. valid input. 2 5 6 13 child (3) Fran: (6) Fran: (5) Ollie: No, I checked for Yes, please add a Yes, that’s a bug. that. check. 3 4 7 8 9 12 (7) Kukla: That fixed it. 10 11
  23. 23. Query Ancestors of #7 SELECT * FROM Comments child JOIN Comments ancestor ON (child.nsleft BETWEEN ancestor.nsleft AND ancestor.nsright) WHERE child.comment_id = 7;
  24. 24. Query Subtree Under #4 (1) Fran: parent What’s the cause of this bug? 1 14 (2) Ollie: (4) Kukla: descendants I think it’s a null We need to check pointer. valid input. 2 5 6 13 (3) Fran: (6) Fran: (5) Ollie: No, I checked for Yes, please add a Yes, that’s a bug. that. check. 3 4 7 8 9 12 (7) Kukla: That fixed it. 10 11
  25. 25. Query Subtree Under #4 SELECT * FROM Comments parent JOIN Comments descendant ON (descendant.nsleft BETWEEN parent.nsleft AND parent.nsright) WHERE parent.comment_id = 4;
  26. 26. Insert New Child of #5 (1) Fran: What’s the cause of this bug? 1 16 14 (2) Ollie: (4) Kukla: I think it’s a null We need to check pointer. valid input. 2 5 6 15 13 (3) Fran: (6) Fran: (5) Ollie: No, I checked for Yes, please add a Yes, that’s a bug. that. check. 3 4 7 8 9 10 11 14 12 (8) Fran: (7) Kukla: I agree! That fixed it. 8 9 10 12 13 11
  27. 27. Insert New Child of #5 UPDATE Comments SET nsleft = CASE WHEN nsleft >= 8 THEN nsleft+2 ELSE nsleft END, nsright = nsright+2 WHERE nsright >= 7; INSERT INTO Comments (nsleft, nsright, author, comment) VALUES (8, 9, 'Fran', 'I agree!'); • Recalculate left values for all nodes to the right of the new child. Recalculate right values for all nodes above and to the right.
  28. 28. Query Immediate Parent of #6 (1) Fran: What’s the cause of this bug? 1 14 (2) Ollie: (4) Kukla: I think it’s a null We need to check pointer. valid input. 2 5 6 13 no node exists (3) Fran: (6) Fran: No, I checked for (5) Ollie: Yes, that’s a bug. Yes, please add a between #4 that. check. 3 4 7 8 9 12 and #6 (7) Kukla: That fixed it. 10 11
  29. 29. Query Immediate Parent of #6 • Parent of #6 is an ancestor who has no descendant who is also an ancestor of #6. SELECT parent.* FROM Comments AS c JOIN Comments AS parent ON (c.nsleft BETWEEN parent.nsleft AND parent.nsright) LEFT OUTER JOIN Comments AS in_between ON (c.nsleft BETWEEN in_between.nsleft AND in_between.nsright AND in_between.nsleft BETWEEN parent.nsleft AND parent.nsright) WHERE c.comment_id = 6 AND in_between.comment_id IS NULL; querying immediate child is a similar problem
  30. 30. Closure Table
  31. 31. Closure Table • Store every path... • ...from parent node to each descendant • ...from each ancestor to child node (same thing)
  32. 32. Closure Table CREATE TABLE TreePaths ( ancestor INT NOT NULL, descendant INT NOT NULL, PRIMARY KEY (ancestor, descendant), FOREIGN KEY(ancestor) REFERENCES Comments(comment_id), FOREIGN KEY(descendant) REFERENCES Comments(comment_id) );
  33. 33. Closure Table illustration (1) Fran: What’s the cause of this bug? (2) Ollie: (4) Kukla: I think it’s a null We need to check pointer. valid input. (3) Fran: (6) Fran: (5) Ollie: No, I checked for Yes, please add a Yes, that’s a bug. that. check. (7) Kukla: That fixed it.
  34. 34. What Does This Look Like? ancestor descendant 1 1 1 2 comment_id author comment 1 3 1 Fran What’s the cause of this bug? 1 4 2 Ollie I think it’s a null pointer. 1 5 3 Fran No, I checked for that. 1 6 4 Kukla We need to check valid input. 1 7 5 Ollie Yes, that’s a bug. 2 2 6 Fran Yes, please add a check 2 3 7 Kukla That fixed it. 3 3 4 4 4 5 requires O(n²) rows 4 6 (but far fewer in practice) 4 5 7 5 6 6 6 7 7 7
  35. 35. Query Descendants of #4 SELECT c.* FROM Comments c JOIN TreePaths t ON (c.comment_id = t.descendant) WHERE t.ancestor = 4;
  36. 36. Paths Starting from #4 (1) Fran: What’s the cause of this bug? (2) Ollie: (4) Kukla: I think it’s a null We need to check pointer. valid input. (3) Fran: (6) Fran: (5) Ollie: No, I checked for Yes, please add a Yes, that’s a bug. that. check. (7) Kukla: That fixed it.
  37. 37. Query Ancestors of #6 SELECT c.* FROM Comments c JOIN TreePaths t ON (c.comment_id = t.ancestor) WHERE t.descendant = 6;
  38. 38. Paths Terminating at #6 (1) Fran: What’s the cause of this bug? (2) Ollie: (4) Kukla: I think it’s a null We need to check pointer. valid input. (3) Fran: (6) Fran: (5) Ollie: No, I checked for Yes, please add a Yes, that’s a bug. that. check. (7) Kukla: That fixed it.
  39. 39. Insert New Child of #5 INSERT INTO Comments VALUES (8, ‘Fran’, ‘I agree!’); INSERT INTO TreePaths (ancestor, descendant) SELECT ancestor, 8 FROM TreePaths WHERE descendant = 5 UNION ALL SELECT 8, 8;
  40. 40. Copy Paths from Parent (1) Fran: What’s the cause of this bug? (2) Ollie: (4) Kukla: I think it’s a null We need to check pointer. valid input. (3) Fran: (6) Fran: (5) Ollie: No, I checked for Yes, please add a Yes, that’s a bug. that. check. (8) Fran: (7) Kukla: I agree! That fixed it.
  41. 41. Delete Child #7 DELETE FROM TreePaths WHERE descendant = 7;
  42. 42. Delete Paths Terminating at #7 (1) Fran: What’s the cause of this bug? (2) Ollie: (4) Kukla: I think it’s a null We need to check pointer. valid input. (3) Fran: (6) Fran: (5) Ollie: No, I checked for Yes, please add a Yes, that’s a bug. that. check. (7) Kukla: That fixed it.
  43. 43. Delete Paths Terminating at #7 (1) Fran: What’s the cause of this bug? (2) Ollie: (4) Kukla: I think it’s a null We need to check pointer. valid input. (3) Fran: (6) Fran: (5) Ollie: No, I checked for Yes, please add a Yes, that’s a bug. that. check. (7) Kukla: That fixed it.
  44. 44. Delete Subtree Under #4 DELETE FROM TreePaths WHERE descendant IN (SELECT descendant FROM TreePaths WHERE ancestor = 4);
  45. 45. Delete Paths Terminating at Any Node Under #4 (1) Fran: What’s the cause of this bug? (2) Ollie: (4) Kukla: I think it’s a null We need to check pointer. valid input. (3) Fran: (6) Fran: (5) Ollie: No, I checked for Yes, please add a Yes, that’s a bug. that. check. (7) Kukla: That fixed it.
  46. 46. Delete Paths Terminating at Any Node Under #4 (1) Fran: What’s the cause of this bug? (2) Ollie: (4) Kukla: I think it’s a null We need to check pointer. valid input. (3) Fran: (6) Fran: (5) Ollie: No, I checked for Yes, please add a Yes, that’s a bug. that. check. (7) Kukla: That fixed it.
  47. 47. Path Length ancestor descendant length • Add a length column 1 1 1 2 0 1 • MAX(length) is depth of tree 1 1 3 4 2 1 • Makes it easier to query 1 5 2 1 6 2 1 7 3 immediate parent or child: 2 2 0 2 3 1 SELECT c.* 3 3 0 FROM Comments c JOIN TreePaths t 4 4 0 ON (c.comment_id = t.descendant) 4 4 5 6 1 1 WHERE t.ancestor = 4 4 7 2 AND t.length = 1; 5 5 0 6 6 0 6 7 1 7 7 0
  48. 48. Choosing the Right Design Query Query Delete Insert Move Referential Design Tables Child Subtree Node Node Subtree Integrity Adjacency 1 Easy Hard Easy Easy Easy Yes List Path 1 Easy Easy Easy Easy Easy No Enumeration Nested Sets 1 Hard Easy Hard Hard Hard No Closure 2 Easy Easy Easy Easy Hard Yes Table
  49. 49. PHP Tree Class
  50. 50. Hierarchical Test Data • Integrated Taxonomic Information System • http://ITIS.GOV/ • Free authoritative taxonomic information on plants, animals, fungi, microbes • 490,032 scientific names
  51. 51. California Poppy Kingdom: Plantae Division: Tracheobionta Class: Magnoliophyta Order: Magnoliopsida unranked: Magnoliidae unranked: Papaverales Family: Papaveraceae Genus: Eschscholzia Species: Eschscholzia californica id 18956
  52. 52. California Poppy: ITIS Entry SELECT * FROM Hierarchy WHERE hierarchy_string LIKE ‘%-18956’; hierarchy_string 202422-564824-18061-18063-18064-18879-18880-18954-18956 ITIS data uses ...but I converted it path enumeration to closure table
  53. 53. Hierarchical Data Classes abstract class ZendX_Db_Table_TreeTable extends Zend_Db_Table_Abstract { public function fetchTreeByRoot($rootId, $expand) public function fetchBreadcrumbs($leafId) }
  54. 54. Hierarchical Data Classes class ZendX_Db_Table_Row_TreeRow extends Zend_Db_Table_Row_Abstract { public function addChildRow($childRow) public function getChildren() } class ZendX_Db_Table_Rowset_TreeRowset extends Zend_Db_Table_Rowset_Abstract { public function append($row) }
  55. 55. Using TreeTable class ItisTable extends ZendX_Db_Table_TreeTable { protected $_name = “longnames”; protected $_closureName = “treepaths”; } $itis = new ItisTable();
  56. 56. Breadcrumbs $breadcrumbs = $itis->fetchBreadcrumbs(18956); foreach ($breadcrumbs as $crumb) { print $crumb->completename . “ > ”; } Plantae > Tracheobionta > Magnoliophyta > Magnoliopsida > Magnoliidae > Papaverales > Papaveraceae > Eschscholzia > Eschscholzia californica >
  57. 57. Breadcrumbs SQL SELECT a.* FROM longnames AS a INNER JOIN treepaths AS c ON a.tsn = c.a WHERE (c.d = 18956) ORDER BY c.l DESC
  58. 58. How Does it Perform? • Query profile = 0.0006 sec • MySQL EXPLAIN: table type key ref rows extra c ref tree_dl const 9 Using where; Using index a eq_ref primary c.a 1
  59. 59. Dump Tree $tree = $itis->fetchTreeByRoot(18880); // Papaveraceae print_tree($tree); function print_tree($tree, $prefix = ‘’) { print “{$prefix} {$tree->completename}n”; foreach ($tree->getChildren() as $child) { print_tree($child, “{$prefix} ”); } }
  60. 60. Dump Tree Result Papaveraceae Romneya Platystigma Romneya coulteri Platystigma linearis Romneya trichocalyx Glaucium Dendromecon Glaucium corniculatum Dendromecon harfordii Glaucium flavum Dendromecon rigida Chelidonium Eschscholzia Chelidonium majus Eschscholzia californica Bocconia Eschscholzia glyptosperma Bocconia frutescens Eschscholzia hypecoides Stylophorum Eschscholzia lemmonii Stylophorum diphyllum Eschscholzia lobbii Stylomecon Eschscholzia minutiflora Stylomecon heterophylla Eschscholzia parishii Canbya Eschscholzia ramosa Canbya aurea Eschscholzia rhombipetala Canbya candida Eschscholzia caespitosa Chlidonium Chlidonium majus etc...
  61. 61. Dump Tree SQL expanding specific nodes SELECT d.*, p.a AS _parent FROM treepaths AS c INNER JOIN longnames AS d ON c.d = d.tsn LEFT JOIN treepaths AS p ON p.d = d.tsn AND p.a IN (202422, 564824, 18053, 18020) AND p.l = 1 show immediate WHERE (c.a = 202422) children of these nodes AND (p.a IS NOT NULL OR d.tsn = 202422) ORDER BY c.l, d.completename;
  62. 62. How Does it Perform? • Query profile = 0.30 sec • MySQL EXPLAIN: table type key ref rows extra Using index; c ref tree_adl const 114240 Using temporary; Using filesort d eq_ref primary c.d 1 p ref tree_dl c.d, const 1 Using where; Using index
  63. 63. Demo Time!
  64. 64. What’s Next for TreeTable? • http://framework.zend.com/svn/framework/ extras/incubator • Refactor to support multiple tree solutions • Consider solutions in NoSQL too
  65. 65. SQL Antipatterns • Shipping in July from Pragmatic Bookshelf • Download the Beta e-book now http://www.pragprog.com/titles/bksqla/
  66. 66. Copyright 2008-2010 Bill Karwin www.slideshare.net/billkarwin Released under a Creative Commons 3.0 License: http://creativecommons.org/licenses/by-nc-nd/3.0/ You are free to share - to copy, distribute and transmit this work, under the following conditions: Attribution. Noncommercial. No Derivative Works. You must attribute this You may not use this work You may not alter, work to Bill Karwin. for commercial purposes. transform, or build upon this work.
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×