Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SQL Server Query or Tool to show Hierarchical Data

We have a general organizational table structure, think of it s a Tree or Pyramid Hierarchy. We basically have multiple "trees" we want to show. On for one company, one for another ETC.

Does anyone know of a good way to display this data? SQL Query would be nice, doubt it would be possible, but I wouldn't be against using some OTS tool (preferably free). I would also like to avoid some type of report. I don't need an actual solution just need to know if its possible. So if you say SQL if you can give me a 2 table example of showing a root a leave I would be happy.

Structure is pretty general

alt text

And each table is linked through a surrogate key CompanyID, CompanyGroupID, etc.

Any suggestions on ways we could display/query for this data? Last resort is to write a quick C# Windows Application...

We would like to see it in tree form:

--                      1-Company
--                     /        \
--             CompanyGroupA   CompanyGroupB
--            /       \              \
--  CompanyStoreA1 CompanyStoreA1 CompanyStoreB
--    /      \            /    \
--Employee   A            B     C   

In attempt to please the masses here is an example test script to populate the query.

DECLARE @Company table (id int, name varchar(40) )
INSERT @Company VALUES (1,'Living Things' )  
INSERT @Company VALUES (2,'Boring Company' )  


DECLARE @CompanyGroup table (id int, name varchar(40), CompanyID int)
INSERT @CompanyGroup VALUES (1,'Pets',1 ) 
INSERT @CompanyGroup VALUES (2,'Humans',1 ) 
INSERT @CompanyGroup VALUES (3,'Electronics',2 ) 
INSERT @CompanyGroup VALUES (4,'Food',2 ) 


DECLARE @CompanyStore table (id int, name varchar(40), CompanyGroupID int)
INSERT @CompanyStore VALUES (1,'PetsStoreA',1 ) 
INSERT @CompanyStore VALUES (2,'PetsStoreB',1 ) 
INSERT @CompanyStore VALUES (3,'PetsStoreC',1 ) 
INSERT @CompanyStore VALUES (4,'PetsStoreD', 1) 
INSERT @CompanyStore VALUES (5,'HumansStore',2 ) 
INSERT @CompanyStore VALUES (6,'FoodStore',3 ) 

The final solution was pretty awesome I modified the usp_DrawTree to accept varchar vs ints because i had to make my query ids unique. I then just did a select/union all and built the parent child relationship.

select * into #TreeData from (
  select ID='C' + cast(id as varchar(10)),
       ParentID=null,
       DataForBox=name + '(' + cast(id as varchar(10)) + ')',
       ExtraInfo='', 
       SortColumn=name
  from Company c 
 )
union all (
  select ID='CG' + cast(id as varchar(10)),
       ParentID=cg.CompanyID ,
       DataForBox=name + '(' + cast(id as varchar(10)) + ')',
       ExtraInfo='', 
       SortColumn=name
  from CompanyGroup cg join Company c on c.ID=cg.CompanyID 
  ) 
//union all rest of hierarchy
)
like image 580
Nix Avatar asked Jul 30 '10 12:07

Nix


People also ask

What type of SQL query handles hierarchical data?

A hierarchical query is a type of SQL query that handles hierarchical model data. They are special cases of more general recursive fixpoint queries, which compute transitive closures. In standard SQL:1999 hierarchical queries are implemented by way of recursive common table expressions (CTEs).

How do I create a hierarchical query in SQL Server?

Use hierarchyid as a data type to create tables with a hierarchical structure, or to describe the hierarchical structure of data that is stored in another location. Use the hierarchyid functions in Transact-SQL to query and manage hierarchical data.

How do you display hierarchy data?

Representing hierarchical information is quite straightforward; in the majority of cases the information designer will either use a tree diagram (or a variant of a tree diagram) or a treemap to display the data in an efficient format for the user.

Can we use SQL for hierarchical database?

Hierarchical query is a type of SQL query that is commonly leveraged to produce meaningful results from hierarchical data. Hierarchical data is defined as a set of data items that are related to each other by hierarchical relationships.


2 Answers

Brad Schulz to the rescue in the form of usp_DrawTree.

alt text

like image 102
8kb Avatar answered Nov 15 '22 11:11

8kb


you don't provide any table structure, so here is a sample of a recursive CTE processing a tree structure:

--go through a nested table supervisor - user table and display the chain
DECLARE @Contacts table (id varchar(6), first_name varchar(10), reports_to_id varchar(6))
INSERT @Contacts VALUES ('1','Jerome', NULL )  -- tree is as follows:
INSERT @Contacts VALUES ('2','Joe'   ,'1')     --                      1-Jerome
INSERT @Contacts VALUES ('3','Paul'  ,'2')     --                     /        \
INSERT @Contacts VALUES ('4','Jack'  ,'3')     --              2-Joe           9-Bill
INSERT @Contacts VALUES ('5','Daniel','3')     --            /       \              \
INSERT @Contacts VALUES ('6','David' ,'2')     --     3-Paul          6-David       10-Sam
INSERT @Contacts VALUES ('7','Ian'   ,'6')     --    /      \            /    \
INSERT @Contacts VALUES ('8','Helen' ,'6')     -- 4-Jack  5-Daniel   7-Ian    8-Helen
INSERT @Contacts VALUES ('9','Bill ' ,'1')     --
INSERT @Contacts VALUES ('10','Sam'  ,'9')     --

DECLARE @Root_id  char(4)

--get complete tree---------------------------------------------------
SET @Root_id=null
PRINT '@Root_id='+COALESCE(''''+@Root_id+'''','null')
;WITH StaffTree AS
(
    SELECT 
        c.id, c.first_name, c.reports_to_id, c.reports_to_id as Manager_id, cc.first_name AS Manager_first_name, 1 AS LevelOf
        FROM @Contacts                  c
            LEFT OUTER JOIN @Contacts  cc ON c.reports_to_id=cc.id
        WHERE c.id=@Root_id OR (@Root_id IS NULL AND c.reports_to_id IS NULL)
    UNION ALL
        SELECT 
            s.id, s.first_name, s.reports_to_id, t.id, t.first_name, t.LevelOf+1
        FROM StaffTree            t
            INNER JOIN @Contacts  s ON t.id=s.reports_to_id
    WHERE s.reports_to_id=@Root_id OR @Root_id IS NULL OR t.LevelOf>1
)
SELECT * FROM StaffTree


--get all below 2---------------------------------------------------
SET @Root_id=2
PRINT '@Root_id='+COALESCE(''''+@Root_id+'''','null')
;WITH StaffTree AS
(
    SELECT 
        c.id, c.first_name, c.reports_to_id, c.reports_to_id as Manager_id, cc.first_name AS Manager_first_name, 1 AS LevelOf
        FROM @Contacts                  c
            LEFT OUTER JOIN @Contacts  cc ON c.reports_to_id=cc.id
        WHERE c.id=@Root_id OR (@Root_id IS NULL AND c.reports_to_id IS NULL)
    UNION ALL
        SELECT 
            s.id, s.first_name, s.reports_to_id, t.id, t.first_name, t.LevelOf+1
        FROM StaffTree            t
            INNER JOIN @Contacts  s ON t.id=s.reports_to_id
    WHERE s.reports_to_id=@Root_id OR @Root_id IS NULL OR t.LevelOf>1
)
SELECT * FROM StaffTree

--get all below 6---------------------------------------------------
SET @Root_id=6
PRINT '@Root_id='+COALESCE(''''+@Root_id+'''','null')
;WITH StaffTree AS
(
    SELECT 
        c.id, c.first_name, c.reports_to_id, c.reports_to_id as Manager_id, cc.first_name AS Manager_first_name, 1 AS LevelOf
        FROM @Contacts                  c
            LEFT OUTER JOIN @Contacts  cc ON c.reports_to_id=cc.id
        WHERE c.id=@Root_id OR (@Root_id IS NULL AND c.reports_to_id IS NULL)
    UNION ALL
        SELECT 
            s.id, s.first_name, s.reports_to_id, t.id, t.first_name, t.LevelOf+1
        FROM StaffTree            t
            INNER JOIN @Contacts  s ON t.id=s.reports_to_id
    WHERE s.reports_to_id=@Root_id OR @Root_id IS NULL OR t.LevelOf>1
)
SELECT * FROM StaffTree

OUTPUT:

@Root_id=null
id     first_name reports_to_id Manager_id Manager_first_name LevelOf
------ ---------- ------------- ---------- ------------------ -----------
1      Jerome     NULL          NULL       NULL               1
2      Joe        1             1          Jerome             2
9      Bill       1             1          Jerome             2
10     Sam        9             9          Bill               3
3      Paul       2             2          Joe                3
6      David      2             2          Joe                3
7      Ian        6             6          David              4
8      Helen      6             6          David              4
4      Jack       3             3          Paul               4
5      Daniel     3             3          Paul               4

(10 row(s) affected)

@Root_id='2   '
id     first_name reports_to_id Manager_id Manager_first_name LevelOf
------ ---------- ------------- ---------- ------------------ -----------
2      Joe        1             1          Jerome             1
3      Paul       2             2          Joe                2
6      David      2             2          Joe                2
7      Ian        6             6          David              3
8      Helen      6             6          David              3
4      Jack       3             3          Paul               3
5      Daniel     3             3          Paul               3

(7 row(s) affected)

@Root_id='6   '
id     first_name reports_to_id Manager_id Manager_first_name LevelOf
------ ---------- ------------- ---------- ------------------ -----------
6      David      2             2          Joe                1
7      Ian        6             6          David              2
8      Helen      6             6          David              2

(3 row(s) affected)

EDIT based on OP's given tables and data:

try something like this:

SET NOCOUNT ON
DECLARE @Company table (id int, name varchar(40) )
INSERT @Company VALUES (1,'Living Things' )  
INSERT @Company VALUES (2,'Boring Company' )  

DECLARE @CompanyGroup table (id int, name varchar(40), CompanyID int)
INSERT @CompanyGroup VALUES (1,'Pets'        ,1 ) 
INSERT @CompanyGroup VALUES (2,'Humans'      ,1 ) 
INSERT @CompanyGroup VALUES (3,'Electronics' ,2 ) 
INSERT @CompanyGroup VALUES (4,'Food'        ,2 ) 

DECLARE @CompanyStore table (id int, name varchar(40), CompanyGroupID int)
INSERT @CompanyStore VALUES (1,'PetsStoreA'   ,1 ) 
INSERT @CompanyStore VALUES (2,'PetsStoreB'   ,1 ) 
INSERT @CompanyStore VALUES (3,'PetsStoreC'   ,1 ) 
INSERT @CompanyStore VALUES (4,'PetsStoreD'   ,1) 
INSERT @CompanyStore VALUES (5,'HumansStore'  ,2 ) 
INSERT @CompanyStore VALUES (6,'FoodStore'    ,3 ) 

--not provided by the OP, so I made it up
DECLARE @CompanyEmployees table (id int, name varchar(10), reports_to_id int, CompanyStoreID int)
INSERT @CompanyEmployees VALUES (1,'Jerome', NULL ,1)  -- tree is as follows:
INSERT @CompanyEmployees VALUES (2,'Joe'   ,1     ,1)     --                      PetsStoreA             PetsStoreB         PetStoreC          FoodStore
INSERT @CompanyEmployees VALUES (3,'Paul'  ,2     ,1)     --                      1-Jerome                 11-Alan           14-Ben              18-apple
INSERT @CompanyEmployees VALUES (4,'Jack'  ,3     ,1)     --                     /        \                /      \           /                  /     \
INSERT @CompanyEmployees VALUES (5,'Daniel',3     ,1)     --              2-Joe           9-Bill         12-Ally  13-Abby    15-Bill         19-pear   20-grape
INSERT @CompanyEmployees VALUES (6,'David' ,2     ,1)     --            /       \              \                             /      \                    /
INSERT @CompanyEmployees VALUES (7,'Ian'   ,6     ,1)     --     3-Paul          6-David       10-Sam                     16-Bjorn  17-Benny           21-rasin
INSERT @CompanyEmployees VALUES (8,'Helen' ,6     ,1)     --    /      \            /    \
INSERT @CompanyEmployees VALUES (9,'Bill ' ,1     ,1)     -- 4-Jack  5-Daniel   7-Ian    8-Helen
INSERT @CompanyEmployees VALUES (10,'Sam'  ,9     ,1)     --
INSERT @CompanyEmployees VALUES (11,'Alan' ,NULL  ,2)     --to see all trees, scroll--->>
INSERT @CompanyEmployees VALUES (12,'Ally' ,11    ,2)     --
INSERT @CompanyEmployees VALUES (13,'Abby' ,11    ,2)     --
INSERT @CompanyEmployees VALUES (14,'Ben'  ,NULL  ,3)     --     
INSERT @CompanyEmployees VALUES (15,'Bill' ,14    ,3)     --
INSERT @CompanyEmployees VALUES (16,'Bjorn',15    ,3)     --
INSERT @CompanyEmployees VALUES (17,'Benny',15    ,3)     --
INSERT @CompanyEmployees VALUES (18,'apple',NULL  ,6)     --
INSERT @CompanyEmployees VALUES (19,'pear' ,18    ,6)     --
INSERT @CompanyEmployees VALUES (20,'grape',18    ,6)     --
INSERT @CompanyEmployees VALUES (21,'rasin',21    ,6)     --
SET NOCOUNT OFF

;WITH StaffTree AS
(
    SELECT 
        c.id, c.name, c.reports_to_id, c.reports_to_id as Manager_id, cc.name AS Manager_name, 1 AS LevelOf, c.CompanyStoreID
        FROM @CompanyEmployees                c
            LEFT OUTER JOIN @CompanyEmployees cc ON c.reports_to_id=cc.id
        WHERE c.reports_to_id IS NULL
    UNION ALL
        SELECT 
            s.id, s.name, s.reports_to_id, t.id, t.name, t.LevelOf+1, s.CompanyStoreID
        FROM StaffTree                    t
            INNER JOIN @CompanyEmployees  s ON t.id=s.reports_to_id
)
SELECT
    c.id AS CompanyID, c.name AS CompanyName
        ,g.id AS CompanyGroupID, g.name AS CompanyName
        ,s.id AS CompanyStoreID, s.name AS CompanyStoreName
        ,t.id AS EmployeeID, t.name as EmployeeName, t.Manager_id, t.Manager_name, t.LevelOf
    FROM @Company                c
        LEFT JOIN @CompanyGroup  g ON c.id=g.CompanyID
        LEFT JOIN @CompanyStore  s ON g.id=s.CompanyGroupID
        LEFT JOIN StaffTree      t ON s.id=t.CompanyStoreID
    ORDER BY c.name,g.name,s.name,s.ID,t.LevelOf,t.name

OUTPUT:

CompanyID CompanyName    CompanyGroupID CompanyName CompanyStoreID CompanyStoreName EmployeeID  EmployeeName Manager_id  Manager_name LevelOf
--------- -------------- -------------- ----------- -------------- ---------------- ----------- ------------ ----------- ------------ -------
2         Boring Company 3              Electronics 6              FoodStore        18          apple        NULL        NULL         1
2         Boring Company 3              Electronics 6              FoodStore        20          grape        18          apple        2
2         Boring Company 3              Electronics 6              FoodStore        19          pear         18          apple        2
2         Boring Company 4              Food        NULL           NULL             NULL        NULL         NULL        NULL         NULL
1         Living Things  2              Humans      5              HumansStore      NULL        NULL         NULL        NULL         NULL
1         Living Things  1              Pets        1              PetsStoreA       1           Jerome       NULL        NULL         1
1         Living Things  1              Pets        1              PetsStoreA       9           Bill         1           Jerome       2
1         Living Things  1              Pets        1              PetsStoreA       2           Joe          1           Jerome       2
1         Living Things  1              Pets        1              PetsStoreA       6           David        2           Joe          3
1         Living Things  1              Pets        1              PetsStoreA       3           Paul         2           Joe          3
1         Living Things  1              Pets        1              PetsStoreA       10          Sam          9           Bill         3
1         Living Things  1              Pets        1              PetsStoreA       5           Daniel       3           Paul         4
1         Living Things  1              Pets        1              PetsStoreA       8           Helen        6           David        4
1         Living Things  1              Pets        1              PetsStoreA       7           Ian          6           David        4
1         Living Things  1              Pets        1              PetsStoreA       4           Jack         3           Paul         4
1         Living Things  1              Pets        2              PetsStoreB       11          Alan         NULL        NULL         1
1         Living Things  1              Pets        2              PetsStoreB       13          Abby         11          Alan         2
1         Living Things  1              Pets        2              PetsStoreB       12          Ally         11          Alan         2
1         Living Things  1              Pets        3              PetsStoreC       14          Ben          NULL        NULL         1
1         Living Things  1              Pets        3              PetsStoreC       15          Bill         14          Ben          2
1         Living Things  1              Pets        3              PetsStoreC       17          Benny        15          Bill         3
1         Living Things  1              Pets        3              PetsStoreC       16          Bjorn        15          Bill         3
1         Living Things  1              Pets        4              PetsStoreD       NULL        NULL         NULL        NULL         NULL

(23 row(s) affected)

EDIT after OP's edit stating that We would like to see it in tree form.

The question is tagged sql-server-2008 and hierarchical-data, and the OP wants to do complex formatting for display f the data. However this type of processing and display is not the realm of TSQL and is a very clear example of where the application language should process and format flat data provided by a SQL query. I have provided such a query that could be used by an application to build a visual tree display. Also note that the simple tree example (no more than two children per parent) might not be very realistic and when many children exist for a single parent, the display will become difficult to construct and not pleasing to the eye.

like image 32
KM. Avatar answered Nov 15 '22 12:11

KM.