Hierarchical Query for Sybase database

Team

I am using DBartisan tool for sybase database.
I have a table that has below fields Employee_ID,EMP_Name,First_Nm,Last_Nm,Emp_Designation,Employee's_Manager
is it possible to retrieve hierarchical data based on below fields
Emp_Designation will have Soft Engg,SSE,Team Lead, Architect,Vice President,President,CEO etc
Employee's_Manager names are again referred under EMP_Name where again he would be reporting to another manager under
"Employee's_Manager" column

Will it be possible to retrieve hierarchical data based on above fields/values by comparing to Employee's_Manager designation
Any help appreciated.

What kind of Sybase database are you using - ASE, ADS, IQ, SQL Anywhere etc.?
And what's the version of your Sybase database?

Thanks for replying
Please find the details .

Sybase is ASE(Adaptive Server Enterprise/15.7/EBF )
For query analysis using Embarcadero DBArtisan 9.1.2 version

Thanks in advance

Thanks. You have a modern version of Sybase.
Try the following query.

--
with emp (emp_id, emp_name, emp_desg, emp_mgr) as (
    select 10, 'Adam', 'CEO',        null union all
    select 20, 'Brian', 'President', 10   union all
    select 30, 'Chuck', 'VP',        10   union all
    select 40, 'Dan',  'Architect',  20   union all
    select 50, 'Eric', 'SSE',        20   union all
    select 60, 'Fred', 'Team Lead',  30   union all
    select 70, 'Greg', 'SE',         60
),
hier_tree (emp_id, emp_name, emp_desg, emp_mgr, level, path) as (
    -- Seed query
    select emp_id, emp_name, emp_desg, emp_mgr, 1 as level, cast('/' + emp_name as varchar) as path
      from emp
     where emp_mgr is null
    union all
    -- Recursive query
    select e.emp_id, e.emp_name, e.emp_desg, e.emp_mgr, h.level + 1, cast(h.path + '/' + e.emp_name as varchar)
      from hier_tree h, emp e
     where h.emp_id = e.emp_mgr
)
select *
  from hier_tree

I do not have Sybase db to test it; I tested it in SQL Server 2008.
Since Sybase and SQL Server share a common code base, it should work in Sybase, maybe with a few minor tweaks.

Here's my execution:

3>
4> select @@version
5> go

------------------------------------------------------------------------------------------------------------------------
Microsoft SQL Server 2008 R2 (SP3) - 10.50.6000.34 (X64)
        Aug 19 2014 12:21:34
        Copyright (c) Microsoft Corporation
        Enterprise Edition (64-bit) on Windows NT 6.1 <X64> (Build 7601: Service Pack 1)


(1 rows affected)
1>
2> --
3> with emp (emp_id, emp_name, emp_desg, emp_mgr) as (
4>     select 10, 'Adam', 'CEO',        null union all
5>     select 20, 'Brian', 'President', 10   union all
6>     select 30, 'Chuck', 'VP',        10   union all
7>     select 40, 'Dan',  'Architect',  20   union all
8>     select 50, 'Eric', 'SSE',        20   union all
9>     select 60, 'Fred', 'Team Lead',  30   union all
10>    select 70, 'Greg', 'SE',         60
11> ),
12> hier_tree (emp_id, emp_name, emp_desg, emp_mgr, level, path) as (
13>     -- Seed query
14>     select emp_id, emp_name, emp_desg, emp_mgr, 1 as level, cast('/' + emp_name as varchar) as path
15>       from emp
16>      where emp_mgr is null
17>     union all
18>     -- Recursive query
19>     select e.emp_id, e.emp_name, e.emp_desg, e.emp_mgr, h.level + 1, cast(h.path + '/' + e.emp_name as varchar)
20>       from hier_tree h, emp e
21>      where h.emp_id = e.emp_mgr
22> )
23> select *
24>   from hier_tree
25> go
emp_id      emp_name emp_desg  emp_mgr     level       path
----------- -------- --------- ----------- ----------- ------------------------------
         10 Adam     CEO              NULL           1 /Adam
         20 Brian    President          10           2 /Adam/Brian
         30 Chuck    VP                 10           2 /Adam/Chuck
         60 Fred     Team Lead          30           3 /Adam/Chuck/Fred
         70 Greg     SE                 60           4 /Adam/Chuck/Fred/Greg
         40 Dan      Architect          20           3 /Adam/Brian/Dan
         50 Eric     SSE                20           3 /Adam/Brian/Eric

(7 rows affected)
1>
2>
3>

A few notes follow:

(1) Sybase might require you to use the keyword "recursive" after the keyword "with". So "with emp..." becomes "with recursive emp...". It is optional as per ANSI SQL standard. Some databases like PostgreSQL require it; others like Oracle and DB2 don't.

(2) The "with emp" subquery was created by me to generate the data on-the-fly, without having to create a table. You already have the emp data in a table, so you may want to use that table. If you do that, then your query begins from "hier_tree". So: "with hier_tree..." or "with recursive hier_tree..." - whichever works.

(3) Concatenation operator "+" should work in Sybase.

(4) Not sure about the "cast()" function in Sybase. SQL Server throws an error message if I don't use it.

Thanks for the reply .

The WITH or WITH RECURSIVE does not work with Sybase ASE
Looks like this is only supported by Sybase SQL Anywhere, not by the "professional" Adaptive Server Enterprise .

It showing incorrect syntax near the keyword both "With and With recursive option "

Too bad Sybase does not support that syntax, since it is part of ANSI standard.
If, however, you have a procedural language in Sybase then you should be able to implement the pure SQL query into the procedural language.

Procedural languages in a database are the ones that allow you to have programming constructs like loops (for, while, do..until etc.) or branches (if...then). Examples of procedural languages are PL/SQL in Oracle, PL/pgSQL in PostgreSQL. You can write procedures/functions in procedural languages. Not sure if Sybase has procedural language built in it.

An explanation of the SQL query follows:

  • The "seed query" fetches only the records that have "NULL" for the "emp_mgr". These are the records at the top of the hierarchies. In my sample data, there is only one such record (Adam). It's the root node and its level is set to 1.

  • The "recursive query" then fetches the immediate children of the root node by joining it back to the "emp" table. So, for "Adam", we fetch records that have "Adam" as emp_mgr. That would be "Brian" and "Chuck". They are at level 1+1 = 2. Now our "hier_tree" has only two records - Brian and Chuck.

  • The "recursive query" part of "hier_tree" is run again now. This time, it is joined with "emp" table again to find the immediate child nodes of (Brian, Chuck). That would be (Dan, Eric, Fred). Their level is 2+1 = 3. Now our "hier_tree" has three records (Dan, Eric, Fred).

  • The "recursive query" part of "hier_tree" is run again now. This time, it is joined with "emp" table again to find the immediate child nodes of (Dan, Eric, Fred). So we get (Greg). His level is 3+1 = 4. Now our "hier_tree" has only one record (Greg).

  • The "recursive query" part of "hier_tree" is run again now. It is joined with "emp" table to find the immediate child nodes of (Greg). This query returns no records, because there are no records in "emp" that has Greg as the manager. So, the execution of "hier_tree" is terminated. This is our exit condition - the "recursive query" part returning 0 records.

As you probably know, every recursive function must: (a) have an exit condition and (b) move nearer and nearer to the exit condition in each recursive call. That's exactly what is happening in the SQL query.

(I don't know how or why SQL Server shows the output as depth-first and not breadth-first traversal. In Oracle, I can specify the type of traversal.)

You could probably use temporary tables to store the intermediate results that are in "hier_tree" in the example. Hope that helps.

Over the weekend, I was able to install a very basic free, developer's edition of SAP Sybase ASE 16.0 on my Debian 8 OS.
The SQL implementation is quite primitive compared to those by the big boys. No "with" subqueries (Common Table Expressions), no recursive hierarchical queries, no group by extensions like rollup or cube, no object relational SQL and no support for analytic / window functions!

Here's my testcase for implementation of the hierarchical data querying mentioned in my earlier post:

declare @depth int
declare @rowcount int
set @depth = 1
while (1 = 1)
begin
    insert into #hier_tree (id, name, desg, mgr, depth, root, path)
    select e.id, e.name, e.desg, e.mgr,
           h.depth + 1 as depth,
           h.root,
           h.path + '/' + e.name as path
      from #hier_tree h, emp e
     where h.id = e.mgr
       and h.depth = @depth
    set @rowcount = @@ROWCOUNT
    if (@rowcount = 0)
        break
    set @depth = @depth + 1
end
go

The complete testcase and its execution follows:

1>
2> select @@version as ver
3> go

  ver
--------------------------------------------------------------------------------------------------------------------------------------------
  Adaptive Server Enterprise/16.0 GA PL01/EBF 22544 SMP/P/x86_64/Enterprise Linux/ase160sp00pl01/3523/64-bit/FBO/Tue Apr 15 13:24:31 2014

(1 row affected)
1>
2>
3> --
4> create table emp (
5>     id    int         not null,
6>     name  varchar(20) not null,
7>     desg  varchar(20) not null,
8>     mgr   int         null
9> )
10> go
1>
2> insert into emp (id, name, desg, mgr)
3> select  10, 'Adam',    'CEO',       null union all
4> select  20, 'Brian',   'President', 10   union all
5> select  30, 'Chuck',   'VP',        10   union all
6> select  40, 'Dan',     'Architect', 20   union all
7> select  50, 'Eric',    'SSE',       20   union all
8> select  60, 'Fred',    'Team Lead', 30   union all
9> select  70, 'Greg',    'SE',        60   union all
10> select 100, 'Patrick', 'Executive', null union all
11> select 200, 'Quinn',   'President', 100  union all
12> select 300, 'Roger',   'VP',        100
13> go
(10 rows affected)
1>
2> -- Two hierarchies exist; one starting from Adam and the other from Patrick
3> select * from emp
4> go
 id          name                 desg                 mgr
 ----------- -------------------- -------------------- -----------
          10 Adam                 CEO                         NULL
          20 Brian                President                     10
          30 Chuck                VP                            10
          40 Dan                  Architect                     20
          50 Eric                 SSE                           20
          60 Fred                 Team Lead                     30
          70 Greg                 SE                            60
         100 Patrick              Executive                   NULL
         200 Quinn                President                    100
         300 Roger                VP                           100

(10 rows affected)
1>
2> -- Create the temp table
3> --
4> create table #hier_tree (
5>     id       int             not null,
6>     name     varchar(20)     not null,
7>     desg     varchar(20)     not null,
8>     mgr      int             null,
9>     depth    int             not null,
10>     root     varchar(20)     not null,
11>     path     varchar(60)    not null
12> )
13> go
1>
2> -- Seed query
3> insert into #hier_tree (id, name, desg, mgr, depth, root, path)
4> select e.id, e.name, e.desg, e.mgr,
5>        1 as depth,
6>        e.name as root,
7>        '/' + e.name as path
8>   from emp e
9>  where e.mgr is null
10> go
(2 rows affected)
1>
2> --
3> -- The top level root nodes (Adam and Patrick) should be loaded now
4> --
5> select * from #hier_tree
6> go
 id          name                 desg                 mgr         depth       root                 path
 ----------- -------------------- -------------------- ----------- ----------- -------------------- ------------------------------------------------------------
          10 Adam                 CEO                         NULL           1 Adam                 /Adam
         100 Patrick              Executive                   NULL           1 Patrick              /Patrick

(2 rows affected)
1>
2> --
3> -- Now load the entire hierarchy tree using a while loop, recursively joining the temp table with emp
4> --
5> declare @depth int
6> declare @rowcount int
7> set @depth = 1
8> while (1 = 1)
9> begin
10>     insert into #hier_tree (id, name, desg, mgr, depth, root, path)
11>     select e.id, e.name, e.desg, e.mgr,
12>            h.depth + 1 as depth,
13>            h.root,
14>            h.path + '/' + e.name as path
15>       from #hier_tree h, emp e
16>      where h.id = e.mgr
17>        and h.depth = @depth
18>     set @rowcount = @@ROWCOUNT
19>     if (@rowcount = 0)
20>         break
21>     set @depth = @depth + 1
22> end
23> go
(1 row affected)
(4 rows affected)
(1 row affected)
(1 row affected)
(3 rows affected)
(1 row affected)
(1 row affected)
(1 row affected)
(1 row affected)
(1 row affected)
(0 rows affected)
(1 row affected)
1>
2> --
3> -- Check the results
4> --
5> select * from #hier_tree
6> go
 id          name                 desg                 mgr         depth       root                 path
 ----------- -------------------- -------------------- ----------- ----------- -------------------- ------------------------------------------------------------
          10 Adam                 CEO                         NULL           1 Adam                 /Adam
         100 Patrick              Executive                   NULL           1 Patrick              /Patrick
          20 Brian                President                     10           2 Adam                 /Adam/Brian
          30 Chuck                VP                            10           2 Adam                 /Adam/Chuck
         200 Quinn                President                    100           2 Patrick              /Patrick/Quinn
         300 Roger                VP                           100           2 Patrick              /Patrick/Roger
          40 Dan                  Architect                     20           3 Adam                 /Adam/Brian/Dan
          50 Eric                 SSE                           20           3 Adam                 /Adam/Brian/Eric
          60 Fred                 Team Lead                     30           3 Adam                 /Adam/Chuck/Fred
          70 Greg                 SE                            60           4 Adam                 /Adam/Chuck/Fred/Greg

(10 rows affected)
1>
2> --
3> -- Sort the data so each hierarchy tree is seen clearly
4> --
5> select * from #hier_tree order by root, depth
6> go
 id          name                 desg                 mgr         depth       root                 path
 ----------- -------------------- -------------------- ----------- ----------- -------------------- ------------------------------------------------------------
          10 Adam                 CEO                         NULL           1 Adam                 /Adam
          20 Brian                President                     10           2 Adam                 /Adam/Brian
          30 Chuck                VP                            10           2 Adam                 /Adam/Chuck
          40 Dan                  Architect                     20           3 Adam                 /Adam/Brian/Dan
          50 Eric                 SSE                           20           3 Adam                 /Adam/Brian/Eric
          60 Fred                 Team Lead                     30           3 Adam                 /Adam/Chuck/Fred
          70 Greg                 SE                            60           4 Adam                 /Adam/Chuck/Fred/Greg
         100 Patrick              Executive                   NULL           1 Patrick              /Patrick
         300 Roger                VP                           100           2 Patrick              /Patrick/Roger
         200 Quinn                President                    100           2 Patrick              /Patrick/Quinn

(10 rows affected)
1>
2> --
3> -- Testcase over. Time to clean up...
4> --
5> drop table #hier_tree
6> go
1>
2> --
3> drop table emp
4> go
1>
2>

Hope that helps.

1 Like