Your SlideShare is downloading. ×
  • Like
]project-open[ Data-Model “Categories”
Upcoming SlideShare
Loading in...5

Thanks for flagging this SlideShare!

Oops! An error has occurred.


Now you can save presentations on your phone or tablet

Available for both IPhone and Android

Text the download link to your phone

Standard text messaging rates apply

]project-open[ Data-Model “Categories”


This tutorial explains the purpose and use of “categories” in ]project-open[. Categories are the most widely used data-structure in the system. They are used to represent the status and type of …

This tutorial explains the purpose and use of “categories” in ]project-open[. Categories are the most widely used data-structure in the system. They are used to represent the status and type of business objects and as general classifiers.

Published in Technology
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
    Be the first to like this
No Downloads


Total Views
On SlideShare
From Embeds
Number of Embeds



Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

    No notes for slide


  • 1. ]po[ “Categories” The ]project-open[ Data-Model , Frank Bergmann, 2010-09-22 This tutorial explains the purpose and use of “categories” in ]project-open[. Categories are the most widely used data-structure in the system. They are used to represent the status and type of business objects and as general classifiers. In other words: Categories represent the values of most GUI drop-down boxes in the system.
  • 2. GUI Example
    • The screenshot at the right shows a typical drop-down box in ]project-open[. The values correspond to the lifecycle of a project from acquisition through operations to invoicing.
    • Hierarchical structure: Please note the sub-states of “Potential” and “Closed”. When searching for “Potential” projects, ]po[ will include projects in status “Inquiring”, “Qualifying” etc.,
    (The list of Project States from / )
  • 3. Problems Solved by “Categories”
    • Nearly every business object in every business application requires a field for:
      • Status – The lifecycle of the object: Typical values include “to be approved”, “finished”, “deleted” etc.
      • Type – The sub-type of the object
    • Following best practices in database modeling, these fields are usually moved into a separate table.
    • As a result, 2 out of 3 tables in most data-models are such status or type tables. ]po[ includes 130 business objects, so we would need some 500 status and type tables. A lot of repetitive code needs to be written to maintain the values of these tables, while providing little business value.
    • Most status and type table are “flat”, while states and types in the real world are closer to trees or DAGs (= Directed Acyclic Graphs - trees with multiple inheritance). However, hierarchical queries on trees and DAGs are usually very slow and cumbersome.
    • ]po[ provides a unified system to represent states and types
    • ]po[ includes support for high-speed category queries
    (The list of Project States from / )
  • 4. Solution: "Categories"
    • Conventional/ traditional database design normally
    • includes “foreign key tables” defining the type and
    • status of an object.
    • ] po [ takes a different approach in order to minimize
    • the number of database tables. We use a single
    • "im_categories" table for all types of states:
    • Pros:
    • The total number of DB tables is reduced to a third.
    • A single maintenance screen can deal with all categories.
    • Built-in features:
      • Translation/Localization
      • Hierarchical categories
      • Common GUI widgets
    • Referential Integrity is enforced
    • Cons:
    • In theory it is possible to assign the wrong Category to a field. However this has never happened in practice yet.
    object_type_id object_status_id object s object_type_id name object_type s description name description ... object_type_id object_status_id object s name description ... category_id name im_categories description category_type ] po [ DB-Design: All type and status information is stored in a single "im_categories" table. The "Classical" DB-Design: Every table has it's own tables for type, status and similar information. object_status_id name object_stat e s description
  • 5. Hierarchical Categories
    • To get closed to the real-world, categories can be hierarchical.
    • Every arrow in the diagram at the left corresponds to one entry in the im_category_hierarchy table.
    Potential “ Top” Open Closed Inquiring Qualifying Declined Delivered Quoting Quote Out Invoiced Deleted Canceled (The list of Project States from / ) Every arrow corresponds to one entry in the im_category_hierarchy table
  • 6. Hierarchical Categories
    • The table “im_category_hierarchy” contains the mapping between parents and children of the category hierarchy.
    • The SQL and the results on the right hand side show the values from the Project Status example.
    im_category_hierarchy object_type_id object_status_id object name description ... category_id name im_categories description category_type ] po [ DB-Design: The table im_category_hierarchy contains the is-parent-of relationship on categories. parent_category_id child_category_id SELECT parent_id, im_category_from_id(parent_id) as parent, hild_id, im_category_from_id(child_id) as child FROM im_category_hierarchy WHERE parent_id in ( select project_status_id from im_project_status ); parent_id | parent | child_id | child -----------+-----------+----------+------------ 81 | Closed | 83 | Canceled 81 | Closed | 77 | Declined 81 | Closed | 82 | Deleted 81 | Closed | 78 | Delivered 81 | Closed | 79 | Invoiced 71 | Potential | 72 | Inquiring 71 | Potential | 73 | Qualifying 71 | Potential | 75 | Quote Out 71 | Potential | 74 | Quoting
  • 7. DAG Hierarchies (“Multiple Inheritance”)
    • ]project-open[ categories even allow for “multiple inheritance”. That is, one category can have more then one parent.
    • In the case of “Company Type” above, the “CustOrIntl” category (Customer or Internal) represents a super-category of both “Customer” and “Internal”.
    • Please note the additional arrows in the diagram. Each arrow represents an entry in the im_category_hierarchy table.
    Customer “ Top” Internal Provider Translation Customer Software Customer Freelance Provider Office Equipment Provider Law Company CustOrIntl Every arrow corresponds to one entry in the im_category_hierarchy table Every arrow corresponds to one entry in the im_category_hierarchy table SELECT parent_id, im_category_from_id(parent_id) as parent, child_id, im_category_from_id(child_id) as child FROM im_category_hierarchy WHERE parent_id in (select company_type_id from im_company_types) ORDER BY parent, child; parent_id | parent | child_id | child -----------+------------+----------+--------------------------- 57 | Customer | 10245 | IT Consultancy 57 | Customer | 10244 | Law Company 57 | Customer | 54 | MLV Translation Agency 57 | Customer | 55 | Software Company 10246 | CustOrIntl | 57 | Customer 10246 | CustOrIntl | 53 | Internal 10246 | CustOrIntl | 10245 | IT Consultancy 10246 | CustOrIntl | 10244 | Law Company 10246 | CustOrIntl | 54 | MLV Translation Agency 10246 | CustOrIntl | 55 | Software Company 56 | Provider | 58 | Freelance Provider 56 | Provider | 59 | Office Equipment Provider Every arrow corresponds to one entry in the im_category_hierarchy table Every arrow corresponds to one entry in the im_category_hierarchy table
  • 8. Categories Shortcut Functions
    • im_category_from_id(category_id): Returns the category name for the ID. This function is useful, because it saves an SQL join in the SQL clause. Otherwise SQL clauses could get quite complex.
    • im_sub_category(caregory_id): Returns the set of sub-categories.
    • im_category_new(category_id, category, category_type): Creates a new “category” of type “Category”.
    SELECT im_category_from_id( 71 ) ; im_category_from_id --------------------- Potential (1 row) select * from im_sub_categories(57); im_sub_categories ------------------- 54 55 57 10244 10245 select im_category_new( nextval('im_categories_seq')::integer, 'Test', 'Intranet Project Status‘ ); im_category_new ----------------- 0
  • 9. SQL Queries with Hierarchical Categories
    • Let’s assume we want to list all “Potential” projects. This listing allows us for example to calculate the value of the projects in the “pre-sales pipeline”.
    • We use the im_sub_categories() function in an “in” sub-query to return all project states below and including “potential”.
    SELECT p.project_nr, p.project_name, im_category_from_id(p.project_status_id) as status, im_category_from_id(p.project_type_id) as type, p.cost_quotes_cache FROM im_projects p WHERE p.project_status_id in (select * from im_sub_categories(71)); project_nr | project_name | status | type | cost_quotes_cache ------------+--------------------------+-----------+-------------+------------------ 2010_0036 | Motor Development | Potential | Development | 12000 .00 2010_0002 | Rollout ABC | Potential | Rollout | 50000 .00 2010_0007 | Resource Planning Detail | Quoting | Development | 6500 .00 (3 rows)
  • 10. Categories as Constants
    • Categories are used as constants in many parts of ]project-open[.
    • This is necessary because there is business logic that behaves differently for “potential” vs. “open” projects (for example, you can configure in ]po[ whether users are allowed to log hours on potential projects or not).
    • Constants are defined by convention in the head of the TCL libraries.
    • Usually, the top level of a category type is used as constants.
    • Changing these constants will break the business logic. However, the user can add sub-categories without problems.
    „ All“ Open Inquiring Qualifying Quoting Quote Out Declined Delivered Invoiced Constants, don't change! Freely Configurable Deleted Canceled Closed Potential
  • 11. Categories SQL Examples “Cookbook”
    • Select Everything About Categories
    • select
    • c.*,
    • im_category_from_id(aux_int1) as aux_int1_cat,
    • im_category_from_id(aux_int2) as aux_int2_cat,
    • h.parent_id,
    • im_category_from_id(h.parent_id) as parent
    • from
    • im_categories c
    • left outer join im_category_hierarchy h
    • on (c.category_id = h.child_id)
    • where
    • c.category_type = 'Intranet Cost Types'
    • order by
    • category_type,
    • category_id;
    Categories Definition -- We use categories as a universal storage for business -- object states and types, instead of a zillion of tables -- like 'im_project_status' and 'im_project_type'. create sequence im_categories_seq start 100000; create table im_categories ( category_id integer constraint im_categories_pk primary key, category varchar(50) not null, category_description varchar(4000), category_type varchar(50), category_gif varchar(100) default 'category', enabled_p char(1) default 't' constraint im_enabled_p_ck check(enabled_p in ('t','f')), -- used to indicate "abstract" -- super-categorys that are not -- valid values for objects. -- For example: "Translation -- Project" is not a project_type, -- but a class of project_types. parent_only_p char(1) default 'f' constraint im_parent_only_p_ck check(parent_only_p in ('t','f')) ); Category Hierarchy -- Optional system to put categories in a hierarchy. -- This table stores the "transitive closure" of the -- is-a relationship between categories in a kind of matrix. -- Let's asume: B isa A and C isa B. So we'll store -- the tupels (C,A), (C,B) and (B,A). -- This structure is a very fast structure for asking: -- -- "is category A a subcategory of B?" -- -- but requires n^2 storage space in the worst case and -- it's a mess retracting settings from the hierarchy. -- We won't have very deep hierarchies, so storage complexity -- is not going to be a problem. create table im_category_hierarchy ( parent_id integer constraint im_parent_category_fk references im_categories, child_id integer constraint im_child_category_fk references im_categories, constraint category_hierarchy_un unique (parent_id, child_id) ); Extract Categories Without Join -- A helper functions to make our queries easier to read create or replace function im_category_from_id (integer) returns varchar as ' DECLARE p_category_id alias for $1; v_category varchar(50); BEGIN select category into v_category from im_categories where category_id = p_category_id; return v_category; end;' language 'plpgsql'; -- Example: -- select im_category_from_id(48); Create a New Category Entry insert into im_categories ( category_id, category, category_type, category_description, enabled_p, aux_int1, aux_int2, aux_string1, aux_string2 ) values ( :category_id, :category, :category_type, :category_description, :enabled_p, :aux_int1, :aux_int2, :aux_string1, :aux_string2 );
  • 12. Frank Bergmann [email_address]