SlideShare a Scribd company logo
1 of 82
Download to read offline
Practical
object-oriented
models in SQL
Bill Karwin
PostgreSQL Conference West 09 • 2009/10/17
Me
• 20+ years experience
  •   Application/SDK developer
  •   Support, Training, Proj Mgmt
  •   C, Java, Perl, PHP

• SQL maven
  •   MySQL, PostgreSQL, InterBase
  •   Zend Framework
  •   Oracle, SQL Server, IBM DB2, SQLite

• Community contributor
Object-Oriented vs. Relational


•   Impedance mismatch
•   OO operates on instances;
    RDBMS operates on sets
•   Compromise either
    OO strengths or
    relational strengths
Example database
Entity-Attribute-Value
If you try and take a cat apart to see how it works, the
first thing you have on your hands is a non-working cat.
                                  — Richard Dawkins
Entity-Attribute-Value
 • Objective:          extensibility
    •    Variable sets of attributes

bug_id   bug_type   priority   description     severity       sponsor

                               crashes when      loss of
 1234       BUG       high
                                   saving     functionality


 3456     FEATURE     low      support XML                    Acme Corp.
Entity-Attribute-Value

             Meta-Table


CREATE TABLE eav (

 bug_id

 
 INT NOT NULL,

 attr_name

 VARCHAR(20) NOT NULL,

 attr_value

 VARCHAR(100),

 PRIMARY KEY (bug_id, attr_name),

 FOREIGN KEY (bug_id) REFERENCES Bugs(bug_id)
);
                 mixing data
                with metadata
Entity-Attribute-Value

What does this look like?

bug_id   attr_name                   attr_value
 1234     priority                      high
 1234    description           crashes when saving
 1234     severity              loss of functionality
 3456     priority                       low
 3456    description                 support XML
 3456     sponsor                    Acme Corp.
Entity-Attribute-Value

         Problems: names


bug_id    attr_name                   attr_value
 1234       created                   2008-04-01
 3456     created_date                2008-04-01
Entity-Attribute-Value

         Problems: values


bug_id    attr_name                   attr_value
 1234     created_date                2008-02-31
 3456     created_date                  banana
Entity-Attribute-Value

      Problems: constraints
                                           NOT NULL


• Difficult to enforce mandatory attributes
 •   SQL constraints apply to columns, not rows

 •   No way to declare that a row must exist with a
     certain attr_name value (‘created_date’)
Entity-Attribute-Value

        Problems: constraints
• Difficult to use lookup tables
  bug_id        attr_name                    attr_value
      1234         priority                     high
      3456         priority                   medium
      5678         priority                    banana


  •    Constraints apply to all rows in the column, not
       selected rows depending on value in attr_name
Entity-Attribute-Value

                     Problems: queries
    • Difficult to reconstruct a row of attributes:
        SELECT b.bug_id,                                          need one JOIN
        
   e1.attr_value AS created_date,
        
   e2.attr_value AS priority,                             per attribute
        
   e3.attr_value AS description,
        
   e4.attr_value AS status,
        
   e5.attr_value AS reported_by
        FROM Bugs b
        LEFT JOIN eav e1 ON (b.bug_id = e1.bug_id AND e1.attr_name = ‘created_date’)
        LEFT JOIN eav e2 ON (b.bug_id = e2.bug_id AND e2.attr_name = ‘priority’)
        LEFT JOIN eav e3 ON (b.bug_id = e3.bug_id AND e3.attr_name = ‘description’)
        LEFT JOIN eav e4 ON (b.bug_id = e4.bug_id AND e4.attr_name = ‘status’)
        LEFT JOIN eav e5 ON (b.bug_id = e5.bug_id AND e5.attr_name = ‘reported_by’);


bug_id created_date priority                description           status reported_by
 1234     2008-04-01          high      Crashes when I save.        NEW                Bill
Entity-Attribute-Value

        Solution?



Do it all in application logic?

      ☒☒☒
Entity-Attribute-Value

                 Solution


     Use metadata for metadata
•   Define attributes in columns

•   ALTER TABLE to add attribute columns

•   Define related tables for related types


                      ☑
Entity-Attribute-Value

     Single Table Inheritance
• One table with many columns
• Inapplicable columns are NULL
  CREATE TABLE Issues (
  
   issue_id
 
 SERIAL PRIMARY KEY,
               
  
   created_date
 DATE NOT NULL,
  
   priority

 
 VARCHAR(20),
  
   description
 
 TEXT,
  
   issue_type
 
 CHAR(1) CHECK (issue_type IN (‘B’, ‘F’)),
  
   bug_severity
 VARCHAR(20),
  
   feature_sponsor VARCHAR(100)
  );
Entity-Attribute-Value

       Concrete Table Inheritance
     • Define similar tables for similar types
     • Duplicate common columns in each table
CREATE TABLE Bugs (                  CREATE TABLE Features (

 bug_id
 
 SERIAL PRIMARY KEY,      
 bug_id
 
 SERIAL PRIMARY KEY,

 created_date DATE NOT NULL,        
 created_date DATE NOT NULL,

 priority

 VARCHAR(20),            
 priority

 VARCHAR(20),

 description
 TEXT,                 
 description
 TEXT,

 severity

 VARCHAR(20)             
 sponsor
 VARCHAR(100)
                                                
);                                   );
Entity-Attribute-Value

 Concrete Table Inheritance
• Use UNION to search both tables:
    SELECT u.* FROM (
    
 SELECT issue_id, description FROM Bugs
    
 UNION ALL
    
 SELECT issue_id, description FROM Features
    )u
    WHERE u.description LIKE ...
Entity-Attribute-Value

          Class Table Inheritance
   • Common columns in base table
   • Subtype-specific columns in subtype tables
CREATE TABLE Bugs (                  CREATE TABLE Features (

 issue_id
 INT PRIMARY KEY,         
 issue_id
 INT PRIMARY KEY,

 severity
 VARCHAR(20),             
 sponsor
VARCHAR(100),

 FOREIGN KEY (issue_id)             
 FOREIGN KEY (issue_id)

   REFERENCES Issues (issue_id)     
   REFERENCES Issues (issue_id)
);                                   );
               CREATE TABLE Issues (
               
 issue_id
 SERIAL PRIMARY KEY,
                          
               
 created_date DATE NOT NULL
               
 priority

 VARCHAR(20),
               
 description
 TEXT
               );
Entity-Attribute-Value

     Class Table Inheritance
• Easy to query common columns:
    SELECT * FROM Issues
    WHERE description LIKE ... ;

• Easy to query one subtype at a time:
    SELECT * FROM Issues
    JOIN Bugs USING (issue_id)
    WHERE description LIKE ... ;
Entity-Attribute-Value

    Using EAV appropriately

• If attributes must be fully dynamic
• Enforce constraints in application code
• Don’t try to fetch a single row per entity
• Consider non-relational solutions
  for semi-structured data, e.g. RDF/XML
Polymorphic Associations
    Of course, some people do go both ways.
                        — The Scarecrow
Polymorphic Assocations
• Objective: reference multiple parents
                                  BUGS


  COMMENTS


                                FEATURES
Polymorphic Assocations

           What does this look like?


             comment                                             issue_id
issue_id             comment issue_type issue_id
                id
  ...                                                               ...
               6789   “It crashes”     Bugs         1234
 1234                                                              2345
               9876   “Great idea!”   Features      2345
 Bugs                                                            Features
                           Comments


                                                  mixing data
                                                 with metadata
Polymorphic Assocations

         Problem: constraints
• A FOREIGN KEY constraint
  can’t reference two tables:
  CREATE TABLE Comments (
  
 comment_id
 SERIAL PRIMARY KEY,
  
 comment
 
 TEXT NOT NULL,
  
 issue_type
 VARCHAR(15) NOT NULL
              
  
 
 CHECK (issue_type IN (‘Bugs’, ‘Features’)),
  
 issue_id
 
 INT NOT NULL,
  
 FOREIGN KEY issue_id REFERENCES
  );
             you need this to be
              Bugs or Features
Polymorphic Assocations

         Problem: constraints
• You have to define table with
  no referential integrity:
  CREATE TABLE Comments (
  
 comment_id
 SERIAL PRIMARY KEY,
  
 comment
 
 TEXT NOT NULL,
  
 issue_type
 VARCHAR(15) NOT NULL
              
  
 
 CHECK (issue_type IN (‘Bugs’, ‘Features’)),
  
 issue_id
 
 INT NOT NULL
  );
Polymorphic Assocations

             Problem: queries
•   You can’t use a different table for each row.

    SELECT * FROM Comments
    JOIN
 
 
 
 USING (issue_id);

                               you need this to be
                                Bugs or Features
Polymorphic Assocations

              Problem: queries
•   You have to join to each parent table:
    SELECT *
    FROM Comments c
    LEFT JOIN Bugs b
 ON (c.issue_type = ‘Bugs’
    
 AND c.issue_id = b.issue_id)
    LEFT JOIN Features f ON (c.issue_type = ‘Features’
    
 AND c.issue_id = f.issue_id);



                       you have to get
                      these strings right
Polymorphic Assocations

               Solutions


• Exclusive arcs
• Reverse the relationship
• Base parent table
Polymorphic Assocations

            Exclusive Arcs

CREATE TABLE Comments (

 comment_id
 SERIAL PRIMARY KEY,

 comment
 
 TEXT NOT NULL,

 bug_id

 
 INT NULL,           both columns nullable;

 feature_id

 INT NULL,      exactly one must be non-null

 FOREIGN KEY bug_id

 
 REFERENCES Bugs(bug_id),

 FOREIGN KEY feature_id

 
 REFERENCES Features(feature_id)
);
Polymorphic Assocations

             Exclusive Arcs
•   Referential integrity is enforced
•   But hard to ensure exactly one is non-null
•   Queries are easier:
     SELECT * FROM Comments c
     LEFT JOIN Bugs b
 USING (bug_id)
     LEFT JOIN Features f USING (feature_id);
Polymorphic Assocations

   Reverse the relationship

             BUGS
           COMMENTS                   BUGS



COMMENTS



            FEATURES
           COMMENTS                  FEATURES
Polymorphic Assocations

   Reverse the relationship
CREATE TABLE BugsComments (

 comment_id
 INT NOT NULL,             not many-to-many

 bug_id
 
 
 INT NOT NULL,

 PRIMARY KEY (comment_id),

 FOREIGN KEY (comment_id)

 
 REFERENCES Comments(comment_id),

 FOREIGN KEY (bug_id) REFERENCES Bugs(bug_id)
);

CREATE TABLE FeaturesComments (

 comment_id
 INT NOT NULL,

 feature_id
 
 INT NOT NULL,

 PRIMARY KEY (comment_id),

 FOREIGN KEY (comment_id)

 
 REFERENCES Comments(comment_id),

 FOREIGN KEY (feature_id) REFERENCES Features(feature_id)
);
Polymorphic Assocations

     Reverse the relationship
• Referential integrity is enforced
• Query comments for a given bug:
   SELECT * FROM BugsComments b
   JOIN Comments c USING (comment_id)
   WHERE b.bug_id = 1234;

• Query bug/feature for a given comment:
   SELECT * FROM Comments
   LEFT JOIN (BugsComments JOIN Bugs USING (bug_id))
   
 USING (comment_id)
   LEFT JOIN (FeaturesComments JOIN Features USING (feature_id))
   
 USING (comment_id)
   WHERE comment_id = 9876;
Polymorphic Assocations

       Base parent table


BUGS            ISSUES              FEATURES




            COMMENTS
Polymorphic Assocations

                    Base parent table                         works great with
        
   
   CREATE TABLE Issues (                           Class Table
        
   
      issue_id SERIAL PRIMARY KEY                  Inheritance
        
   
   );

CREATE TABLE Bugs (                         CREATE TABLE Features (
   issue_id INT PRIMARY KEY,                   issue_id INT PRIMARY KEY,
   ...                                         ...
   FOREIGN KEY (issue_id)                      FOREIGN KEY (issue_id)
     REFERENCES Issues(issue_id)                 REFERENCES Issues(issue_id)
);                                          );
        CREATE TABLE Comments (
        
 comment_id SERIAL PRIMARY KEY,
        
 comment
 TEXT NOT NULL,
        
 issue_id
 INT NOT NULL,
                  
        
 FOREIGN KEY (issue_id) REFRENCES Issues(issue_id)
        );
Polymorphic Assocations

           Base parent table
•   Referential integrity is enforced
•   Queries are easier:
     SELECT * FROM Comments
     JOIN Issues USING (issue_id)
     LEFT JOIN Bugs USING (issue_id)
     LEFT JOIN Features USING (issue_id);
Polymorphic Assocations

 Enforcing disjoint subtypes
CREATE TABLE Issues (

 issue_id
 SERIAL PRIMARY KEY,
          

 issue_type
 CHAR(1) CHECK (issue_type IN (‘B’, ‘F’)),

 UNIQUE KEY (issue_id, issue_type)
);                                                    referential
CREATE TABLE Bugs (                                    integrity

 issue_id
 INT PRIMARY KEY,
          

 issue_type
 CHAR(1) CHECK (issue_type = ‘B’),

 ...

 FOREIGN KEY (issue_id, issue_type)

 
 REFERENCES Issues(issue_id, issue_type)
);
Naive Trees
A tree is a tree – how many more do you need to look at?
                                     — Ronald Reagan
Naive Trees


• Objective:        store & query hierarchical data
  •   Categories/subcategories

  •   Bill of materials

  •   Threaded discussions
Naive Trees

What does this look like?
                              (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.
Naive Trees

          Adjacency List design
• 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.
Naive Trees

   Adjacency List strengths

• Easy to inserting a new comment:
   INSERT INTO Comments (parent_id, author, comment)
   
 VALUES (7, ‘Kukla’, ‘Thanks!’);

• Easy to move a subtree to a new position:
   UPDATE Comments SET parent_id = 3
   WHERE comment_id = 6;
Naive Trees

     Adjacency List strengths

• Querying a node’s 
children is easy:
     SELECT * FROM Comments c1
     LEFT JOIN Comments c2
      ON (c2.parent_id = c1.comment_id);

• Querying a node’s parent is easy:
     SELECT * FROM Comments c1
     JOIN Comments c2
      ON (c1.parent_id = c2.comment_id);
Naive Trees

     Adjacency List problems
• Hard to query all descendants in a deep tree:
  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!
Naive Trees

      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;


                  supported in PostgreSQL 8.4
                    & MS SQL Server 2005
Naive Trees

          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.
Naive Trees

  Path Enumeration strengths

• Easy to query ancestors of comment #7:
    SELECT * FROM Comments
    WHERE ‘1/4/6/7/’ LIKE path || ‘%’;

• Easy to query descendants of comment #4:
    SELECT * FROM Comments
    WHERE path LIKE ‘1/4/%’;
Naive Trees

  Path Enumeration strengths

• Easy to add child of comment 7:
     INSERT INTO Comments (author, comment)
     VALUES (‘Ollie’, ‘Good job!’);
     SELECT path FROM Comments
     WHERE comment_id = 7;
     UPDATE Comments
     SET path = $parent_path || LASTVAL() || ‘/’
     WHERE comment_id = LASTVAL();
Naive Trees

                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.
Naive Trees

Nested Sets illustration

                                        (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
Naive Trees

             Nested Sets example

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
Naive Trees

       Nested Sets strengths


• Easy to query all ancestors of comment #7:
     SELECT * FROM Comments child
     JOIN Comments ancestor
      ON (child.nsleft BETWEEN ancestor.nsleft
     
 
 
 
 
 
 AND
 ancestor.nsright)
     WHERE child.comment_id = 7;
Naive Trees

Nested Sets strengths

                                       (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
Naive Trees

      Nested Sets strengths


• Easy to query descendants of comment #4:
    SELECT * FROM Comments parent
    JOIN Comments descendant
     ON (descendant.nsleft BETWEEN parent.nsleft
    
 
 
 
 
 
 
 
 AND
 parent.nsright)
    WHERE parent.comment_id = 4;
Naive Trees

Nested Sets strengths

                                       (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
Naive Trees

       Nested Sets problems
• Hard to insert a new child of comment #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.
Naive Trees

Nested Sets problems

                                       (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
Naive Trees

         Nested Sets problems
• Hard to query the parent of comment #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;


• Parent of #6 is an ancestor who has no
  descendant who is also an ancestor of #6.
• Querying a child is a similar problem.
Naive Trees

             Closure Tables
• Store every path from ancestors to
  descendants
• Requires an additional 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),
  );
Naive Trees

Closure Tables 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.
Naive Trees

     Closure Tables example
                                                      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
Naive Trees

    Closure Tables strengths


• Easy to query descendants of comment #4:
    SELECT c.* FROM Comments c
    JOIN TreePaths t
     ON (c.comment_id = t.descendant)
    WHERE t.ancestor = 4;
Naive Trees

     Closure Tables strengths


• Easy to query ancestors of comment #6:
    SELECT c.* FROM Comments c
    JOIN TreePaths t
     ON (c.comment_id = t.ancestor)
    WHERE t.descendant = 6;
Naive Trees

     Closure Tables strengths

• Easy to insert a new child of comment #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;
Naive Trees

     Closure Tables strengths



• Easy to delete a child comment #7:
     DELETE FROM TreePaths
     WHERE descendant = 7;
Naive Trees

     Closure Tables strengths
• Easy to delete subtree under comment #4:
    DELETE FROM TreePaths
    WHERE descendant IN
    
 (SELECT descendant FROM TreePaths
    
 WHERE ancestor = 4);

• PostgreSQL multi-table DELETE syntax:
    DELETE FROM TreePaths p USING TreePaths a
    WHERE p.descendant = a.descendant
     AND a.ancestor = 4;
Naive Trees

       Closure Tables depth
                                      ancestor descendant   depth
                                         1         1          0



• Add a depth column to make it
                                         1         2          1
                                         1         3          2

  easier to query immediate              1
                                         1
                                                   4
                                                   5
                                                              1
                                                              2

  parent or child:                       1         6          2
                                         1         7          3

  SELECT c.*                             2         2          0
                                         2         3          1
  FROM Comments c JOIN TreePaths t       3         3          0
   ON (c.comment_id = t.descendant)      4         4          0

  WHERE t.ancestor = 4                   4         5          1
                                         4         6          1
   AND t.depth = 1;                      4         7          2
                                         5         5          0
                                         6         6          0
                                         6         7          1
                                         7         7          0
Naive Trees

                 Choose the right design

                        Query    Query Delete     Add   Move Referential
  Design         Tables
                        Child   Subtree Node      Node Subtree Integrity

Adjacency List     1    Easy     Hard      Easy   Easy   Easy     Yes
   Path
                   1    Easy     Easy      Easy   Easy   Easy     No
Enumeration

 Nested Sets       1    Hard     Easy     Hard    Hard   Hard     No

Closure Table      2    Easy     Easy      Easy   Easy   Hard     Yes
Magic Beans
Essentially, all models are wrong, but some are useful.
                                  — George E. P. Box
Magic Beans



• Objective:
  •   Model-View-Controller application development

  •   Object-Relational Mapping (ORM)
Magic Beans

      Active Record

The Golden Hammer of database access
     in most MVC frameworks:
Magic Beans

Model is-a Active Record?
                 Active
                 Record
                                         inheritance
                                            (IS-A)
    Products      Bugs        Comments




 Controller                       View
               aggregation
                (HAS-A)
Magic Beans

        Problem: OO design

• “Model” : Active Record          inheritance
                                      (IS-A)
• Model tied to database schema            coupling

• Product → Bug, or             unclear assignment
                                 of responsibilities
  Bug → Product?

• Models expose Active Record interface,
  not domain-specific interface
                                              poor
                                          encapsulation
Magic Beans

        Problem: MVC design

• Controllers need to know              “T.M.I.” !!
    database structure

•   Database changes require           not “DRY”
    code changes

• http://www.martinfowler.com/bliki/   (appeal to
    AnemicDomainModel.html
                                       authority)
Magic Beans

         Problem: testability

                                               tests are
• Harder to test Model without database          slow

• Need database “fixtures”               tests are
• Fat Controller makes it harder      even slower
  to test business logic

                                  mocking HTTP Request,
                                  scraping HTML output
OO is about decoupling
Magic Beans

Model has-a Active Record(s)
                 Active
                 Record
                                          inheritance
                                             (IS-A)
     Products     Bugs         Comments

                                          composition
                                            (HAS-A)
                BugReport
  Controller                       View
                 (Model)
                 aggregation
                  (HAS-A)
Magic Beans

       Model characteristics

• Extend no base class
• Abstract the database
• Expose only domain-specific interface
• Encapsulate complex business logic
Magic Beans

              Model benefits

• Models are decoupled from DB
  •   Supports mock objects

  •   Supports dependency injection

• Unit-testing Models in isolation is faster
• Unit-testing Thin Controllers is easier
Magic Beans

Decouple your Models
     You can use this design




 ...even in MVC frameworks that
     encourage the antipattern.
Copyright 2008-2009 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.

More Related Content

What's hot

Functional programming with Java 8
Functional programming with Java 8Functional programming with Java 8
Functional programming with Java 8LivePerson
 
jpa-hibernate-presentation
jpa-hibernate-presentationjpa-hibernate-presentation
jpa-hibernate-presentationJohn Slick
 
Entity framework code first
Entity framework code firstEntity framework code first
Entity framework code firstConfiz
 
Java Collections | Collections Framework in Java | Java Tutorial For Beginner...
Java Collections | Collections Framework in Java | Java Tutorial For Beginner...Java Collections | Collections Framework in Java | Java Tutorial For Beginner...
Java Collections | Collections Framework in Java | Java Tutorial For Beginner...Edureka!
 
Java Persistence API (JPA) Step By Step
Java Persistence API (JPA) Step By StepJava Persistence API (JPA) Step By Step
Java Persistence API (JPA) Step By StepGuo Albert
 
Introduction to JPA and Hibernate including examples
Introduction to JPA and Hibernate including examplesIntroduction to JPA and Hibernate including examples
Introduction to JPA and Hibernate including examplesecosio GmbH
 
Hibernate Tutorial
Hibernate TutorialHibernate Tutorial
Hibernate TutorialRam132
 
pytest로 파이썬 코드 테스트하기
pytest로 파이썬 코드 테스트하기pytest로 파이썬 코드 테스트하기
pytest로 파이썬 코드 테스트하기Yeongseon Choe
 
MVC Architecture
MVC ArchitectureMVC Architecture
MVC ArchitecturePrem Sanil
 
Introduction of sql server indexing
Introduction of sql server indexingIntroduction of sql server indexing
Introduction of sql server indexingMahabubur Rahaman
 
REST API 설계
REST API 설계REST API 설계
REST API 설계Terry Cho
 

What's hot (20)

Java Collections
Java  Collections Java  Collections
Java Collections
 
Functional programming with Java 8
Functional programming with Java 8Functional programming with Java 8
Functional programming with Java 8
 
Sql query patterns, optimized
Sql query patterns, optimizedSql query patterns, optimized
Sql query patterns, optimized
 
JPA and Hibernate
JPA and HibernateJPA and Hibernate
JPA and Hibernate
 
jpa-hibernate-presentation
jpa-hibernate-presentationjpa-hibernate-presentation
jpa-hibernate-presentation
 
Entity framework code first
Entity framework code firstEntity framework code first
Entity framework code first
 
Java Collections | Collections Framework in Java | Java Tutorial For Beginner...
Java Collections | Collections Framework in Java | Java Tutorial For Beginner...Java Collections | Collections Framework in Java | Java Tutorial For Beginner...
Java Collections | Collections Framework in Java | Java Tutorial For Beginner...
 
LINQ in C#
LINQ in C#LINQ in C#
LINQ in C#
 
Java Persistence API (JPA) Step By Step
Java Persistence API (JPA) Step By StepJava Persistence API (JPA) Step By Step
Java Persistence API (JPA) Step By Step
 
Introduction to JPA and Hibernate including examples
Introduction to JPA and Hibernate including examplesIntroduction to JPA and Hibernate including examples
Introduction to JPA and Hibernate including examples
 
Hibernate Tutorial
Hibernate TutorialHibernate Tutorial
Hibernate Tutorial
 
pytest로 파이썬 코드 테스트하기
pytest로 파이썬 코드 테스트하기pytest로 파이썬 코드 테스트하기
pytest로 파이썬 코드 테스트하기
 
Java 8 Lambda Expressions
Java 8 Lambda ExpressionsJava 8 Lambda Expressions
Java 8 Lambda Expressions
 
Angular
AngularAngular
Angular
 
Oracle Database Trigger
Oracle Database TriggerOracle Database Trigger
Oracle Database Trigger
 
MVC Architecture
MVC ArchitectureMVC Architecture
MVC Architecture
 
Introduction of sql server indexing
Introduction of sql server indexingIntroduction of sql server indexing
Introduction of sql server indexing
 
REST API 설계
REST API 설계REST API 설계
REST API 설계
 
JPA For Beginner's
JPA For Beginner'sJPA For Beginner's
JPA For Beginner's
 
Collections in-csharp
Collections in-csharpCollections in-csharp
Collections in-csharp
 

Similar to Practical Object Oriented Models In Sql

Sqlantipatternsstrikeback 090421005946-phpapp01
Sqlantipatternsstrikeback 090421005946-phpapp01Sqlantipatternsstrikeback 090421005946-phpapp01
Sqlantipatternsstrikeback 090421005946-phpapp01Chamika Kaluarachchi
 
Introduction to SQL Antipatterns
Introduction to SQL AntipatternsIntroduction to SQL Antipatterns
Introduction to SQL AntipatternsKrishnakumar S
 
Using the latest Java Persistence API 2 Features - Tech Days 2010 India
Using the latest Java Persistence API 2 Features - Tech Days 2010 IndiaUsing the latest Java Persistence API 2 Features - Tech Days 2010 India
Using the latest Java Persistence API 2 Features - Tech Days 2010 IndiaArun Gupta
 
Using the latest Java Persistence API 2.0 features
Using the latest Java Persistence API 2.0 featuresUsing the latest Java Persistence API 2.0 features
Using the latest Java Persistence API 2.0 featuresArun Gupta
 
Understanding
Understanding Understanding
Understanding Arun Gupta
 
[Www.pkbulk.blogspot.com]dbms10
[Www.pkbulk.blogspot.com]dbms10[Www.pkbulk.blogspot.com]dbms10
[Www.pkbulk.blogspot.com]dbms10AnusAhmad
 
Ruby On Rails Pitfalls
Ruby On Rails PitfallsRuby On Rails Pitfalls
Ruby On Rails PitfallsRobin Lu
 
Session #4: Treating Databases as First-Class Citizens in Development
Session #4: Treating Databases as First-Class Citizens in DevelopmentSession #4: Treating Databases as First-Class Citizens in Development
Session #4: Treating Databases as First-Class Citizens in DevelopmentSteve Lange
 
PHX - Session #4 Treating Databases as First-Class Citizens in Development
PHX - Session #4 Treating Databases as First-Class Citizens in DevelopmentPHX - Session #4 Treating Databases as First-Class Citizens in Development
PHX - Session #4 Treating Databases as First-Class Citizens in DevelopmentSteve Lange
 
Nhibernatethe Orm For Net Platform 1226744632929962 8
Nhibernatethe Orm For Net Platform 1226744632929962 8Nhibernatethe Orm For Net Platform 1226744632929962 8
Nhibernatethe Orm For Net Platform 1226744632929962 8Nicolas Thon
 
rails-migrations_1
rails-migrations_1rails-migrations_1
rails-migrations_1brecke
 
]project-open[ Data-Model “Categories”
]project-open[ Data-Model “Categories”]project-open[ Data-Model “Categories”
]project-open[ Data-Model “Categories”Klaus Hofeditz
 

Similar to Practical Object Oriented Models In Sql (20)

Sqlantipatternsstrikeback 090421005946-phpapp01
Sqlantipatternsstrikeback 090421005946-phpapp01Sqlantipatternsstrikeback 090421005946-phpapp01
Sqlantipatternsstrikeback 090421005946-phpapp01
 
Jpa
JpaJpa
Jpa
 
Introduction to SQL Antipatterns
Introduction to SQL AntipatternsIntroduction to SQL Antipatterns
Introduction to SQL Antipatterns
 
Using the latest Java Persistence API 2 Features - Tech Days 2010 India
Using the latest Java Persistence API 2 Features - Tech Days 2010 IndiaUsing the latest Java Persistence API 2 Features - Tech Days 2010 India
Using the latest Java Persistence API 2 Features - Tech Days 2010 India
 
Using the latest Java Persistence API 2.0 features
Using the latest Java Persistence API 2.0 featuresUsing the latest Java Persistence API 2.0 features
Using the latest Java Persistence API 2.0 features
 
Understanding
Understanding Understanding
Understanding
 
Hibernate
HibernateHibernate
Hibernate
 
Attribute
AttributeAttribute
Attribute
 
Django Pro ORM
Django Pro ORMDjango Pro ORM
Django Pro ORM
 
[Www.pkbulk.blogspot.com]dbms10
[Www.pkbulk.blogspot.com]dbms10[Www.pkbulk.blogspot.com]dbms10
[Www.pkbulk.blogspot.com]dbms10
 
Ruby On Rails Pitfalls
Ruby On Rails PitfallsRuby On Rails Pitfalls
Ruby On Rails Pitfalls
 
Session #4: Treating Databases as First-Class Citizens in Development
Session #4: Treating Databases as First-Class Citizens in DevelopmentSession #4: Treating Databases as First-Class Citizens in Development
Session #4: Treating Databases as First-Class Citizens in Development
 
PHX - Session #4 Treating Databases as First-Class Citizens in Development
PHX - Session #4 Treating Databases as First-Class Citizens in DevelopmentPHX - Session #4 Treating Databases as First-Class Citizens in Development
PHX - Session #4 Treating Databases as First-Class Citizens in Development
 
Generic
GenericGeneric
Generic
 
Spring data requery
Spring data requerySpring data requery
Spring data requery
 
Schemadoc
SchemadocSchemadoc
Schemadoc
 
Nhibernatethe Orm For Net Platform 1226744632929962 8
Nhibernatethe Orm For Net Platform 1226744632929962 8Nhibernatethe Orm For Net Platform 1226744632929962 8
Nhibernatethe Orm For Net Platform 1226744632929962 8
 
rails-migrations_1
rails-migrations_1rails-migrations_1
rails-migrations_1
 
S313431 JPA 2.0 Overview
S313431 JPA 2.0 OverviewS313431 JPA 2.0 Overview
S313431 JPA 2.0 Overview
 
]project-open[ Data-Model “Categories”
]project-open[ Data-Model “Categories”]project-open[ Data-Model “Categories”
]project-open[ Data-Model “Categories”
 

More from Karwin Software Solutions LLC (12)

How to Use JSON in MySQL Wrong
How to Use JSON in MySQL WrongHow to Use JSON in MySQL Wrong
How to Use JSON in MySQL Wrong
 
Load Data Fast!
Load Data Fast!Load Data Fast!
Load Data Fast!
 
InnoDB Locking Explained with Stick Figures
InnoDB Locking Explained with Stick FiguresInnoDB Locking Explained with Stick Figures
InnoDB Locking Explained with Stick Figures
 
SQL Outer Joins for Fun and Profit
SQL Outer Joins for Fun and ProfitSQL Outer Joins for Fun and Profit
SQL Outer Joins for Fun and Profit
 
Survey of Percona Toolkit
Survey of Percona ToolkitSurvey of Percona Toolkit
Survey of Percona Toolkit
 
How to Design Indexes, Really
How to Design Indexes, ReallyHow to Design Indexes, Really
How to Design Indexes, Really
 
Percona toolkit
Percona toolkitPercona toolkit
Percona toolkit
 
MySQL 5.5 Guide to InnoDB Status
MySQL 5.5 Guide to InnoDB StatusMySQL 5.5 Guide to InnoDB Status
MySQL 5.5 Guide to InnoDB Status
 
Requirements the Last Bottleneck
Requirements the Last BottleneckRequirements the Last Bottleneck
Requirements the Last Bottleneck
 
Mentor Your Indexes
Mentor Your IndexesMentor Your Indexes
Mentor Your Indexes
 
Sql Injection Myths and Fallacies
Sql Injection Myths and FallaciesSql Injection Myths and Fallacies
Sql Injection Myths and Fallacies
 
Full Text Search In PostgreSQL
Full Text Search In PostgreSQLFull Text Search In PostgreSQL
Full Text Search In PostgreSQL
 

Recently uploaded

A Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software DevelopersA Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software DevelopersNicole Novielli
 
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptxThe Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptxLoriGlavin3
 
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxThe Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxLoriGlavin3
 
Time Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directionsTime Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directionsNathaniel Shimoni
 
Modern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
Modern Roaming for Notes and Nomad – Cheaper Faster Better StrongerModern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
Modern Roaming for Notes and Nomad – Cheaper Faster Better Strongerpanagenda
 
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxPasskey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxLoriGlavin3
 
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024BookNet Canada
 
Long journey of Ruby standard library at RubyConf AU 2024
Long journey of Ruby standard library at RubyConf AU 2024Long journey of Ruby standard library at RubyConf AU 2024
Long journey of Ruby standard library at RubyConf AU 2024Hiroshi SHIBATA
 
2024 April Patch Tuesday
2024 April Patch Tuesday2024 April Patch Tuesday
2024 April Patch TuesdayIvanti
 
Testing tools and AI - ideas what to try with some tool examples
Testing tools and AI - ideas what to try with some tool examplesTesting tools and AI - ideas what to try with some tool examples
Testing tools and AI - ideas what to try with some tool examplesKari Kakkonen
 
Top 10 Hubspot Development Companies in 2024
Top 10 Hubspot Development Companies in 2024Top 10 Hubspot Development Companies in 2024
Top 10 Hubspot Development Companies in 2024TopCSSGallery
 
Design pattern talk by Kaya Weers - 2024 (v2)
Design pattern talk by Kaya Weers - 2024 (v2)Design pattern talk by Kaya Weers - 2024 (v2)
Design pattern talk by Kaya Weers - 2024 (v2)Kaya Weers
 
Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...Farhan Tariq
 
Zeshan Sattar- Assessing the skill requirements and industry expectations for...
Zeshan Sattar- Assessing the skill requirements and industry expectations for...Zeshan Sattar- Assessing the skill requirements and industry expectations for...
Zeshan Sattar- Assessing the skill requirements and industry expectations for...itnewsafrica
 
Bridging Between CAD & GIS: 6 Ways to Automate Your Data Integration
Bridging Between CAD & GIS:  6 Ways to Automate Your Data IntegrationBridging Between CAD & GIS:  6 Ways to Automate Your Data Integration
Bridging Between CAD & GIS: 6 Ways to Automate Your Data Integrationmarketing932765
 
Microsoft 365 Copilot: How to boost your productivity with AI – Part one: Ado...
Microsoft 365 Copilot: How to boost your productivity with AI – Part one: Ado...Microsoft 365 Copilot: How to boost your productivity with AI – Part one: Ado...
Microsoft 365 Copilot: How to boost your productivity with AI – Part one: Ado...Nikki Chapple
 
The Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsThe Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsPixlogix Infotech
 
QCon London: Mastering long-running processes in modern architectures
QCon London: Mastering long-running processes in modern architecturesQCon London: Mastering long-running processes in modern architectures
QCon London: Mastering long-running processes in modern architecturesBernd Ruecker
 
How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.Curtis Poe
 
MuleSoft Online Meetup Group - B2B Crash Course: Release SparkNotes
MuleSoft Online Meetup Group - B2B Crash Course: Release SparkNotesMuleSoft Online Meetup Group - B2B Crash Course: Release SparkNotes
MuleSoft Online Meetup Group - B2B Crash Course: Release SparkNotesManik S Magar
 

Recently uploaded (20)

A Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software DevelopersA Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software Developers
 
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptxThe Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
 
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxThe Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
 
Time Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directionsTime Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directions
 
Modern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
Modern Roaming for Notes and Nomad – Cheaper Faster Better StrongerModern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
Modern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
 
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxPasskey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
 
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
 
Long journey of Ruby standard library at RubyConf AU 2024
Long journey of Ruby standard library at RubyConf AU 2024Long journey of Ruby standard library at RubyConf AU 2024
Long journey of Ruby standard library at RubyConf AU 2024
 
2024 April Patch Tuesday
2024 April Patch Tuesday2024 April Patch Tuesday
2024 April Patch Tuesday
 
Testing tools and AI - ideas what to try with some tool examples
Testing tools and AI - ideas what to try with some tool examplesTesting tools and AI - ideas what to try with some tool examples
Testing tools and AI - ideas what to try with some tool examples
 
Top 10 Hubspot Development Companies in 2024
Top 10 Hubspot Development Companies in 2024Top 10 Hubspot Development Companies in 2024
Top 10 Hubspot Development Companies in 2024
 
Design pattern talk by Kaya Weers - 2024 (v2)
Design pattern talk by Kaya Weers - 2024 (v2)Design pattern talk by Kaya Weers - 2024 (v2)
Design pattern talk by Kaya Weers - 2024 (v2)
 
Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...
 
Zeshan Sattar- Assessing the skill requirements and industry expectations for...
Zeshan Sattar- Assessing the skill requirements and industry expectations for...Zeshan Sattar- Assessing the skill requirements and industry expectations for...
Zeshan Sattar- Assessing the skill requirements and industry expectations for...
 
Bridging Between CAD & GIS: 6 Ways to Automate Your Data Integration
Bridging Between CAD & GIS:  6 Ways to Automate Your Data IntegrationBridging Between CAD & GIS:  6 Ways to Automate Your Data Integration
Bridging Between CAD & GIS: 6 Ways to Automate Your Data Integration
 
Microsoft 365 Copilot: How to boost your productivity with AI – Part one: Ado...
Microsoft 365 Copilot: How to boost your productivity with AI – Part one: Ado...Microsoft 365 Copilot: How to boost your productivity with AI – Part one: Ado...
Microsoft 365 Copilot: How to boost your productivity with AI – Part one: Ado...
 
The Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsThe Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and Cons
 
QCon London: Mastering long-running processes in modern architectures
QCon London: Mastering long-running processes in modern architecturesQCon London: Mastering long-running processes in modern architectures
QCon London: Mastering long-running processes in modern architectures
 
How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.
 
MuleSoft Online Meetup Group - B2B Crash Course: Release SparkNotes
MuleSoft Online Meetup Group - B2B Crash Course: Release SparkNotesMuleSoft Online Meetup Group - B2B Crash Course: Release SparkNotes
MuleSoft Online Meetup Group - B2B Crash Course: Release SparkNotes
 

Practical Object Oriented Models In Sql

  • 1. Practical object-oriented models in SQL Bill Karwin PostgreSQL Conference West 09 • 2009/10/17
  • 2. Me • 20+ years experience • Application/SDK developer • Support, Training, Proj Mgmt • C, Java, Perl, PHP • SQL maven • MySQL, PostgreSQL, InterBase • Zend Framework • Oracle, SQL Server, IBM DB2, SQLite • Community contributor
  • 3. Object-Oriented vs. Relational • Impedance mismatch • OO operates on instances; RDBMS operates on sets • Compromise either OO strengths or relational strengths
  • 5. Entity-Attribute-Value If you try and take a cat apart to see how it works, the first thing you have on your hands is a non-working cat. — Richard Dawkins
  • 6. Entity-Attribute-Value • Objective: extensibility • Variable sets of attributes bug_id bug_type priority description severity sponsor crashes when loss of 1234 BUG high saving functionality 3456 FEATURE low support XML Acme Corp.
  • 7. Entity-Attribute-Value Meta-Table CREATE TABLE eav ( bug_id INT NOT NULL, attr_name VARCHAR(20) NOT NULL, attr_value VARCHAR(100), PRIMARY KEY (bug_id, attr_name), FOREIGN KEY (bug_id) REFERENCES Bugs(bug_id) ); mixing data with metadata
  • 8. Entity-Attribute-Value What does this look like? bug_id attr_name attr_value 1234 priority high 1234 description crashes when saving 1234 severity loss of functionality 3456 priority low 3456 description support XML 3456 sponsor Acme Corp.
  • 9. Entity-Attribute-Value Problems: names bug_id attr_name attr_value 1234 created 2008-04-01 3456 created_date 2008-04-01
  • 10. Entity-Attribute-Value Problems: values bug_id attr_name attr_value 1234 created_date 2008-02-31 3456 created_date banana
  • 11. Entity-Attribute-Value Problems: constraints NOT NULL • Difficult to enforce mandatory attributes • SQL constraints apply to columns, not rows • No way to declare that a row must exist with a certain attr_name value (‘created_date’)
  • 12. Entity-Attribute-Value Problems: constraints • Difficult to use lookup tables bug_id attr_name attr_value 1234 priority high 3456 priority medium 5678 priority banana • Constraints apply to all rows in the column, not selected rows depending on value in attr_name
  • 13. Entity-Attribute-Value Problems: queries • Difficult to reconstruct a row of attributes: SELECT b.bug_id, need one JOIN e1.attr_value AS created_date, e2.attr_value AS priority, per attribute e3.attr_value AS description, e4.attr_value AS status, e5.attr_value AS reported_by FROM Bugs b LEFT JOIN eav e1 ON (b.bug_id = e1.bug_id AND e1.attr_name = ‘created_date’) LEFT JOIN eav e2 ON (b.bug_id = e2.bug_id AND e2.attr_name = ‘priority’) LEFT JOIN eav e3 ON (b.bug_id = e3.bug_id AND e3.attr_name = ‘description’) LEFT JOIN eav e4 ON (b.bug_id = e4.bug_id AND e4.attr_name = ‘status’) LEFT JOIN eav e5 ON (b.bug_id = e5.bug_id AND e5.attr_name = ‘reported_by’); bug_id created_date priority description status reported_by 1234 2008-04-01 high Crashes when I save. NEW Bill
  • 14. Entity-Attribute-Value Solution? Do it all in application logic? ☒☒☒
  • 15. Entity-Attribute-Value Solution Use metadata for metadata • Define attributes in columns • ALTER TABLE to add attribute columns • Define related tables for related types ☑
  • 16. Entity-Attribute-Value Single Table Inheritance • One table with many columns • Inapplicable columns are NULL CREATE TABLE Issues ( issue_id SERIAL PRIMARY KEY, created_date DATE NOT NULL, priority VARCHAR(20), description TEXT, issue_type CHAR(1) CHECK (issue_type IN (‘B’, ‘F’)), bug_severity VARCHAR(20), feature_sponsor VARCHAR(100) );
  • 17. Entity-Attribute-Value Concrete Table Inheritance • Define similar tables for similar types • Duplicate common columns in each table CREATE TABLE Bugs ( CREATE TABLE Features ( bug_id SERIAL PRIMARY KEY, bug_id SERIAL PRIMARY KEY, created_date DATE NOT NULL, created_date DATE NOT NULL, priority VARCHAR(20), priority VARCHAR(20), description TEXT, description TEXT, severity VARCHAR(20) sponsor VARCHAR(100) ); );
  • 18. Entity-Attribute-Value Concrete Table Inheritance • Use UNION to search both tables: SELECT u.* FROM ( SELECT issue_id, description FROM Bugs UNION ALL SELECT issue_id, description FROM Features )u WHERE u.description LIKE ...
  • 19. Entity-Attribute-Value Class Table Inheritance • Common columns in base table • Subtype-specific columns in subtype tables CREATE TABLE Bugs ( CREATE TABLE Features ( issue_id INT PRIMARY KEY, issue_id INT PRIMARY KEY, severity VARCHAR(20), sponsor VARCHAR(100), FOREIGN KEY (issue_id) FOREIGN KEY (issue_id) REFERENCES Issues (issue_id) REFERENCES Issues (issue_id) ); ); CREATE TABLE Issues ( issue_id SERIAL PRIMARY KEY, created_date DATE NOT NULL priority VARCHAR(20), description TEXT );
  • 20. Entity-Attribute-Value Class Table Inheritance • Easy to query common columns: SELECT * FROM Issues WHERE description LIKE ... ; • Easy to query one subtype at a time: SELECT * FROM Issues JOIN Bugs USING (issue_id) WHERE description LIKE ... ;
  • 21. Entity-Attribute-Value Using EAV appropriately • If attributes must be fully dynamic • Enforce constraints in application code • Don’t try to fetch a single row per entity • Consider non-relational solutions for semi-structured data, e.g. RDF/XML
  • 22. Polymorphic Associations Of course, some people do go both ways. — The Scarecrow
  • 23. Polymorphic Assocations • Objective: reference multiple parents BUGS COMMENTS FEATURES
  • 24. Polymorphic Assocations What does this look like? comment issue_id issue_id comment issue_type issue_id id ... ... 6789 “It crashes” Bugs 1234 1234 2345 9876 “Great idea!” Features 2345 Bugs Features Comments mixing data with metadata
  • 25. Polymorphic Assocations Problem: constraints • A FOREIGN KEY constraint can’t reference two tables: CREATE TABLE Comments ( comment_id SERIAL PRIMARY KEY, comment TEXT NOT NULL, issue_type VARCHAR(15) NOT NULL CHECK (issue_type IN (‘Bugs’, ‘Features’)), issue_id INT NOT NULL, FOREIGN KEY issue_id REFERENCES ); you need this to be Bugs or Features
  • 26. Polymorphic Assocations Problem: constraints • You have to define table with no referential integrity: CREATE TABLE Comments ( comment_id SERIAL PRIMARY KEY, comment TEXT NOT NULL, issue_type VARCHAR(15) NOT NULL CHECK (issue_type IN (‘Bugs’, ‘Features’)), issue_id INT NOT NULL );
  • 27. Polymorphic Assocations Problem: queries • You can’t use a different table for each row. SELECT * FROM Comments JOIN USING (issue_id); you need this to be Bugs or Features
  • 28. Polymorphic Assocations Problem: queries • You have to join to each parent table: SELECT * FROM Comments c LEFT JOIN Bugs b ON (c.issue_type = ‘Bugs’ AND c.issue_id = b.issue_id) LEFT JOIN Features f ON (c.issue_type = ‘Features’ AND c.issue_id = f.issue_id); you have to get these strings right
  • 29. Polymorphic Assocations Solutions • Exclusive arcs • Reverse the relationship • Base parent table
  • 30. Polymorphic Assocations Exclusive Arcs CREATE TABLE Comments ( comment_id SERIAL PRIMARY KEY, comment TEXT NOT NULL, bug_id INT NULL, both columns nullable; feature_id INT NULL, exactly one must be non-null FOREIGN KEY bug_id REFERENCES Bugs(bug_id), FOREIGN KEY feature_id REFERENCES Features(feature_id) );
  • 31. Polymorphic Assocations Exclusive Arcs • Referential integrity is enforced • But hard to ensure exactly one is non-null • Queries are easier: SELECT * FROM Comments c LEFT JOIN Bugs b USING (bug_id) LEFT JOIN Features f USING (feature_id);
  • 32. Polymorphic Assocations Reverse the relationship BUGS COMMENTS BUGS COMMENTS FEATURES COMMENTS FEATURES
  • 33. Polymorphic Assocations Reverse the relationship CREATE TABLE BugsComments ( comment_id INT NOT NULL, not many-to-many bug_id INT NOT NULL, PRIMARY KEY (comment_id), FOREIGN KEY (comment_id) REFERENCES Comments(comment_id), FOREIGN KEY (bug_id) REFERENCES Bugs(bug_id) ); CREATE TABLE FeaturesComments ( comment_id INT NOT NULL, feature_id INT NOT NULL, PRIMARY KEY (comment_id), FOREIGN KEY (comment_id) REFERENCES Comments(comment_id), FOREIGN KEY (feature_id) REFERENCES Features(feature_id) );
  • 34. Polymorphic Assocations Reverse the relationship • Referential integrity is enforced • Query comments for a given bug: SELECT * FROM BugsComments b JOIN Comments c USING (comment_id) WHERE b.bug_id = 1234; • Query bug/feature for a given comment: SELECT * FROM Comments LEFT JOIN (BugsComments JOIN Bugs USING (bug_id)) USING (comment_id) LEFT JOIN (FeaturesComments JOIN Features USING (feature_id)) USING (comment_id) WHERE comment_id = 9876;
  • 35. Polymorphic Assocations Base parent table BUGS ISSUES FEATURES COMMENTS
  • 36. Polymorphic Assocations Base parent table works great with CREATE TABLE Issues ( Class Table issue_id SERIAL PRIMARY KEY Inheritance ); CREATE TABLE Bugs ( CREATE TABLE Features ( issue_id INT PRIMARY KEY, issue_id INT PRIMARY KEY, ... ... FOREIGN KEY (issue_id) FOREIGN KEY (issue_id) REFERENCES Issues(issue_id) REFERENCES Issues(issue_id) ); ); CREATE TABLE Comments ( comment_id SERIAL PRIMARY KEY, comment TEXT NOT NULL, issue_id INT NOT NULL, FOREIGN KEY (issue_id) REFRENCES Issues(issue_id) );
  • 37. Polymorphic Assocations Base parent table • Referential integrity is enforced • Queries are easier: SELECT * FROM Comments JOIN Issues USING (issue_id) LEFT JOIN Bugs USING (issue_id) LEFT JOIN Features USING (issue_id);
  • 38. Polymorphic Assocations Enforcing disjoint subtypes CREATE TABLE Issues ( issue_id SERIAL PRIMARY KEY, issue_type CHAR(1) CHECK (issue_type IN (‘B’, ‘F’)), UNIQUE KEY (issue_id, issue_type) ); referential CREATE TABLE Bugs ( integrity issue_id INT PRIMARY KEY, issue_type CHAR(1) CHECK (issue_type = ‘B’), ... FOREIGN KEY (issue_id, issue_type) REFERENCES Issues(issue_id, issue_type) );
  • 39. Naive Trees A tree is a tree – how many more do you need to look at? — Ronald Reagan
  • 40. Naive Trees • Objective: store & query hierarchical data • Categories/subcategories • Bill of materials • Threaded discussions
  • 41. Naive Trees What does this look like? (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.
  • 42. Naive Trees Adjacency List design • 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.
  • 43. Naive Trees Adjacency List strengths • Easy to inserting a new comment: INSERT INTO Comments (parent_id, author, comment) VALUES (7, ‘Kukla’, ‘Thanks!’); • Easy to move a subtree to a new position: UPDATE Comments SET parent_id = 3 WHERE comment_id = 6;
  • 44. Naive Trees Adjacency List strengths • Querying a node’s children is easy: SELECT * FROM Comments c1 LEFT JOIN Comments c2 ON (c2.parent_id = c1.comment_id); • Querying a node’s parent is easy: SELECT * FROM Comments c1 JOIN Comments c2 ON (c1.parent_id = c2.comment_id);
  • 45. Naive Trees Adjacency List problems • Hard to query all descendants in a deep tree: 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!
  • 46. Naive Trees 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; supported in PostgreSQL 8.4 & MS SQL Server 2005
  • 47. Naive Trees 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.
  • 48. Naive Trees Path Enumeration strengths • Easy to query ancestors of comment #7: SELECT * FROM Comments WHERE ‘1/4/6/7/’ LIKE path || ‘%’; • Easy to query descendants of comment #4: SELECT * FROM Comments WHERE path LIKE ‘1/4/%’;
  • 49. Naive Trees Path Enumeration strengths • Easy to add child of comment 7: INSERT INTO Comments (author, comment) VALUES (‘Ollie’, ‘Good job!’); SELECT path FROM Comments WHERE comment_id = 7; UPDATE Comments SET path = $parent_path || LASTVAL() || ‘/’ WHERE comment_id = LASTVAL();
  • 50. Naive Trees 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.
  • 51. Naive Trees Nested Sets illustration (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
  • 52. Naive Trees Nested Sets example 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
  • 53. Naive Trees Nested Sets strengths • Easy to query all ancestors of comment #7: SELECT * FROM Comments child JOIN Comments ancestor ON (child.nsleft BETWEEN ancestor.nsleft AND ancestor.nsright) WHERE child.comment_id = 7;
  • 54. Naive Trees Nested Sets strengths (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
  • 55. Naive Trees Nested Sets strengths • Easy to query descendants of comment #4: SELECT * FROM Comments parent JOIN Comments descendant ON (descendant.nsleft BETWEEN parent.nsleft AND parent.nsright) WHERE parent.comment_id = 4;
  • 56. Naive Trees Nested Sets strengths (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
  • 57. Naive Trees Nested Sets problems • Hard to insert a new child of comment #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.
  • 58. Naive Trees Nested Sets problems (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
  • 59. Naive Trees Nested Sets problems • Hard to query the parent of comment #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; • Parent of #6 is an ancestor who has no descendant who is also an ancestor of #6. • Querying a child is a similar problem.
  • 60. Naive Trees Closure Tables • Store every path from ancestors to descendants • Requires an additional 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), );
  • 61. Naive Trees Closure Tables 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.
  • 62. Naive Trees Closure Tables example 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
  • 63. Naive Trees Closure Tables strengths • Easy to query descendants of comment #4: SELECT c.* FROM Comments c JOIN TreePaths t ON (c.comment_id = t.descendant) WHERE t.ancestor = 4;
  • 64. Naive Trees Closure Tables strengths • Easy to query ancestors of comment #6: SELECT c.* FROM Comments c JOIN TreePaths t ON (c.comment_id = t.ancestor) WHERE t.descendant = 6;
  • 65. Naive Trees Closure Tables strengths • Easy to insert a new child of comment #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;
  • 66. Naive Trees Closure Tables strengths • Easy to delete a child comment #7: DELETE FROM TreePaths WHERE descendant = 7;
  • 67. Naive Trees Closure Tables strengths • Easy to delete subtree under comment #4: DELETE FROM TreePaths WHERE descendant IN (SELECT descendant FROM TreePaths WHERE ancestor = 4); • PostgreSQL multi-table DELETE syntax: DELETE FROM TreePaths p USING TreePaths a WHERE p.descendant = a.descendant AND a.ancestor = 4;
  • 68. Naive Trees Closure Tables depth ancestor descendant depth 1 1 0 • Add a depth column to make it 1 2 1 1 3 2 easier to query immediate 1 1 4 5 1 2 parent or child: 1 6 2 1 7 3 SELECT c.* 2 2 0 2 3 1 FROM Comments c JOIN TreePaths t 3 3 0 ON (c.comment_id = t.descendant) 4 4 0 WHERE t.ancestor = 4 4 5 1 4 6 1 AND t.depth = 1; 4 7 2 5 5 0 6 6 0 6 7 1 7 7 0
  • 69. Naive Trees Choose the right design Query Query Delete Add Move Referential Design Tables Child Subtree Node Node Subtree Integrity Adjacency List 1 Easy Hard Easy Easy Easy Yes Path 1 Easy Easy Easy Easy Easy No Enumeration Nested Sets 1 Hard Easy Hard Hard Hard No Closure Table 2 Easy Easy Easy Easy Hard Yes
  • 70. Magic Beans Essentially, all models are wrong, but some are useful. — George E. P. Box
  • 71. Magic Beans • Objective: • Model-View-Controller application development • Object-Relational Mapping (ORM)
  • 72. Magic Beans Active Record The Golden Hammer of database access in most MVC frameworks:
  • 73. Magic Beans Model is-a Active Record? Active Record inheritance (IS-A) Products Bugs Comments Controller View aggregation (HAS-A)
  • 74. Magic Beans Problem: OO design • “Model” : Active Record inheritance (IS-A) • Model tied to database schema coupling • Product → Bug, or unclear assignment of responsibilities Bug → Product? • Models expose Active Record interface, not domain-specific interface poor encapsulation
  • 75. Magic Beans Problem: MVC design • Controllers need to know “T.M.I.” !! database structure • Database changes require not “DRY” code changes • http://www.martinfowler.com/bliki/ (appeal to AnemicDomainModel.html authority)
  • 76. Magic Beans Problem: testability tests are • Harder to test Model without database slow • Need database “fixtures” tests are • Fat Controller makes it harder even slower to test business logic mocking HTTP Request, scraping HTML output
  • 77. OO is about decoupling
  • 78. Magic Beans Model has-a Active Record(s) Active Record inheritance (IS-A) Products Bugs Comments composition (HAS-A) BugReport Controller View (Model) aggregation (HAS-A)
  • 79. Magic Beans Model characteristics • Extend no base class • Abstract the database • Expose only domain-specific interface • Encapsulate complex business logic
  • 80. Magic Beans Model benefits • Models are decoupled from DB • Supports mock objects • Supports dependency injection • Unit-testing Models in isolation is faster • Unit-testing Thin Controllers is easier
  • 81. Magic Beans Decouple your Models You can use this design ...even in MVC frameworks that encourage the antipattern.
  • 82. Copyright 2008-2009 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.