ORACLE 9I의 HIERARCHICAL QUERY의 ORDER SIBLINGS BY CLAUSE

제품 : ORACLE SERVER
작성날짜 : 2003-10-22
(V9I) Oracle 9i의 Hierarchical query의 ORDER SIBLINGS BY CLAUSE
===============================================================
PURPOSE
이 문서는 Oracle 9i의 new feature인 ORDER SIBLINGS BY 절을
Hierarchical query에 사용하는 예를 통하여 특정 컬럼을 기준으로
Ordering된 형태로 display하는 방법을 보여준다.
Explanation & Example
Hierarchical query를 구현할 때 ORDER BY 절을 사용하는 것은
Oracle 7.1 버젼부터 가능한 것이었다.
그러나, 순서대로 ordering되지 않고 특정 컬럼(emp table의 ename)을
기준으로 ordering하기를 원한다면 <Bulletin:10373>처럼 procedure를
작성하여야만 하였다.
그러나, Oracle 9i 에서는 ORDER BY 절 대신에 ORDER SIBLINGS BY 절을
사용할 수 있어 user-defined stored procedure를 만들 필요가 없게 되었다.
1) Ordering 하기 전의 emp table의 Hierarchical query
SQL> @a
ename EMPNO MGR JOB
KING 7839 PRESIDENT
JONES 7566 7839 MANAGER
SCOTT 7788 7566 ANALYST
ADAMS 7876 7788 CLERK
FORD 7902 7566 ANALYST
SMITH 7369 7902 CLERK
BLAKE 7698 7839 MANAGER
ALLEN 7499 7698 SALESMAN
WARD 7521 7698 SALESMAN
MARTIN 7654 7698 SALESMAN
TURNER 7844 7698 SALESMAN
ename EMPNO MGR JOB
JAMES 7900 7698 CLERK
CLARK 7782 7839 MANAGER
MILLER 7934 7782 CLERK
14 rows selected.
Ordering 하기 전의 a.sql 은 다음과 같다.
col ename format a25
col empno format 99999
col mgr format 99999
col job format a15
select rpad(' ', LEVEL*5) || ename "ename", empno, mgr, job
from emp
start with job='PRESIDENT'
connect by prior empno=mgr;
2) 9i의 new feature인 Hierarchical query를 사용하여 Ordering한 경우
SQL> @new_a
ename EMPNO MGR JOB
KING 7839 PRESIDENT
BLAKE 7698 7839 MANAGER
ALLEN 7499 7698 SALESMAN
JAMES 7900 7698 CLERK
MARTIN 7654 7698 SALESMAN
TURNER 7844 7698 SALESMAN
WARD 7521 7698 SALESMAN
CLARK 7782 7839 MANAGER
MILLER 7934 7782 CLERK
JONES 7566 7839 MANAGER
FORD 7902 7566 ANALYST
ename EMPNO MGR JOB
SMITH 7369 7902 CLERK
SCOTT 7788 7566 ANALYST
ADAMS 7876 7788 CLERK
14 rows selected.
Ordering하기 위해 사용한 new_a.sql 은 다음과 같다.
col ename format a25
col empno format 99999
col mgr format 99999
col job format a15
select rpad(' ', LEVEL*5) || ename "ename", empno, mgr, job
from emp
start with job='PRESIDENT'
connect by prior empno=mgr
order siblings by ename;
Reference Documents
<Bulletin:10373>

Thanks to Kendenny, Boneist and Odie.
Got the point that "Order Siblings by clause" cannot be used with connect by query with analytical function, Thanks Kendenny.
Yes, I now use main query and subquery, however the subquery be just "connect by" and have the all html tags added in the main query.
The below query is working now.
SELECT
CASE WHEN LAG(mylevel,1,0) OVER (ORDER BY myrownum) >= mylevel THEN '<li>'
ELSE
CASE LEAD(mylevel) OVER (ORDER BY myrownum)
WHEN mylevel THEN
CASE WHEN myrownum = 1 THEN '<ul id="sidebarmenu1" '
ELSE '<ul'
END ||'><li>'
ELSE
CASE WHEN myrownum =1 THEN '<ul id="sidebarmenu1"'
ELSE '<ul '
END ||' ><li>'
END
END ||'<a href="'||
   CASE WHEN link_url IS NOT NULL THEN
      link_url||'title="'||menu_item||'"'
ELSE '#"' END ||
'><span>'||short_menu_item||'</span></a>'||
CASE mylevel - LEAD(mylevel,1,1) OVER (ORDER BY myrownum)
WHEN -1 THEN NULL
WHEN 0 THEN '</li>'
ELSE REPLACE(LPAD('*', myleveL-LEAD(mylevel,1,1) OVER (ORDER BY myrownum),'*'), '*','</li></ul></li>')
END ||
CASE WHEN LEAD(mylevel,1,0) OVER (ORDER BY myrownum) = 0 THEN '</ul>'
ELSE NULL END unordered_List,
menu_item, menu_id,
above_menu_id
FROM (
SELECT LEVEL mylevel, ROWNUM myrownum,daevmt.*
FROM dae_vs_my_tasks daevmt
CONNECT BY PRIOR daevmt.menu_id = daevmt.above_menu_id
START WITH daevmt.above_menu_id = 'TOPMENU'
ORDER SIBLINGS BY display_order
) t;
Odie, I tried altering the session for the flag, still the first query was not working.
Thanks again all for your great time in answering me.

Similar Messages

  • ORDER SIBLINGS not working in Forms

    This code run fine in Toad BUT giving me error in forms.
    SELECT
           LEVEL                           LOC_NAME,
           LOCATION_NAME                   LOC_ID,
           LOCATION_ID                     HLOC_ID,
           HEAD_LOC_ID MUDRA
      FROM AS_LOCATION_HDR
    CONNECT BY PRIOR LOCATION_ID = HEAD_LOC_ID
    START WITH HEAD_LOC_ID is null
    ORDER SIBLINGS BY LOC_IDThe error is:
    "Encountered sysbol "SIBLINGS" when expecting one of the following:
    by
    The symbol "by inserted before the "SIBLINGS" to continue.Is the SIBLINGS keyword not acepted in forms? What would be the alternative?
    I am using oracle forms 10G (10.1.2.0.2)

    hi
    Oracle's START WITH and CONNECT BY clauses in the
    SELECT statement automatically traverse a hierarchy.
    Without this feature, a complex self-join would be required to identify
    which rows are logically related to others.
    The START WITH clause identifies the row or rows to be considered
    the starting points, or "roots," of the hierarchy.
    The CONNECT BY PRIOR clause
    then indicates how to identify which rows are related to each other.
    example, the query in Listing A produces a "Reports To" listing from the EMPLOYEES table in the HR sample schema provided by Oracle.
    The LEVEL pseudocolumn indicates how deeply the report is currently nested; here,
    I use it to LPAD the employee names to indent them.
    The START WITH condition states that only employees 101 and 102 are to be considered as starting points.
    The CONNECT BY PRIOR clause then links the employee_id
    column in one row to the manager_id column in the next, to indicate who reports to whom.
    If you run this query in the HR schema,
    you'll notice that the last names are not sorted within the listing for a specific manager;
    they are listed in the order Oracle encountered them in processing the hierarchy.
    If you want the subordinates in alphabetical order,
    you might try to ORDER BY the original last_name column.
    However, this would break up the hierarchy, and turn it back into a flat list of names.
    You might also try to ORDER BY the pseudocolumn LEVEL first,
    which tells how deep a specific row is in the hierarchy. This, too,
    breaks up the hierarchy—all the managers will be listed first, followed by people who report to any of them.
    In Oracle 10g (both releases),
    it's now easy to do this: You can use the new SIBLINGS keyword to create the correct ordering. The syntax is:
    ORDER SIBLINGS BY <expression>So adding the clause:
    ORDER SIBLINGS BY last_nameto the end of the query will preserve
    the hierarchy and also alphabetize the last names within each level.
    Note that the original last_name was used not the alias "Reports To."
    The extra space padding in "Reports To" would affect the sort, so the original must be used.
    Listing B shows the output, both before and after adding ORDER SIBLINGS BY.
    The ORDER SIBLINGS BY clause is valid only in a hierarchical query. The optional SIBLINGS keyword specifies an order that first sorts the parent rows, and then sorts the child rows of each parent for every level within the hierarchy.
    Rows that have duplicate lists of values in the columns specified after the SIBLINGS BY keywords are arbitrarily ordered among the rows with the same list of values and the same parent. If a hierarchical query includes the ORDER BY clause without the SIBLINGS keyword, rows are ordered according to the sort specifications that follow the ORDER BY keywords. Neither the ORDER BY clause nor the ORDER SIBLINGS BY option to the ORDER BY clause is required in hierarchical queries.
    The hierarchical query in the following example returns the subset of rows in the hierarchical data set whose root is Goyal, as listed in the topic Hierarchical Clause. This query includes the ORDER SIBLINGS BY clause to sort by name the employees who report to the same manager:
    SELECT empid, name, mgrid, LEVEL
       FROM employee
          START WITH name = 'Goyal'
          CONNECT BY PRIOR empid = mgrid
       ORDER SIBLINGS BY name;The rows returned by this query are sorted in the following order:
             empid name             mgrid       level
             16 Goyal               17           1
             12 Henry               16           2
              7 O'Neil              12           3
              9 Shoeman             12           3
              8 Smith               12           3
             14 Scott               16           2
             11 Zander              16           2
              6 Barnes              11           3
              5 McKeough            11           3
    9 row(s) retrieved.Here the START WITH clause returned the Goyal row at the root of this hierarchy. Two subsequent CONNECT BY steps (marked as 2 and 3 in the level pseudocolumn) returned three sets of sibling rows:
    Henry, Scott, and Zander are siblings whose parent is Goyal;
    O'Neil, Shoeman, and Smith are siblings whose parent is Henry;
    Barnes and McKeough are siblings whose parent is Zander.
    The next CONNECT BY step returned no rows, because the rows for which level = 3 are leaf nodes within this hierarchy. At this point in the execution of the query, the ORDER SIBLINGS BY clause was applied to the result set, sorting the rows in the order shown above.
    Because the sort key, name, is a VARCHAR column, the returned rows within each set of siblings are in the ASCII order of their employee.name values. Only the sets of siblings that are leaf nodes in the hierarchy of returned rows appear consecutively in the sorted result set, because the managers are immediately followed by the employees who report to them, rather than by their siblings. An exception in this example is Scott, whose child nodes form an empty set.
    The SIBLINGS keyword in the ORDER BY clause is an extension to the ISO standard syntax for the SQL language. The SELECT statement fails with an error if you include the SIBLINGS keyword in the ORDER BY clause of a query or subquery that does not include a valid CONNECT BY clause. hope this helps u.
    sarah

  • Hierarchical query with many-to-many relationship

    I have read with interest the creative solutions to complex hierarchical queries posted previously; they have been instructive but have not quite addressed this scenario.
    We have a hierarchy table H, with columns for ID, name, parentID, and other attributes.
    Within this table are a number of independent hierarchies, each existing for a different purpose.
    We have a master list of hierarchies in table T which describes the purpose of each hierarchy, provides some default attributes which the nodes can inherit, and stores a unique id for each hierarchy and a pointer to the root node of the corresponding hierarchy in table H.
    We have a master list of items M, with identically named columns to those in H, along with many other attributes.
    The members of table M ALL belong to EACH of the Hierarchies. So we have a link table I to define the intersection of H and M.
    So the leaf nodes of H are really containers for the list of elements from M which may be attached to them.
    The universe of M is very volatile, with new members being added, old ones deleted, and existing ones being reclassified frequently from node to node within each hierarchy. Since the hierarchies have to be built to handle every possible scenario, so that the members of M can always find a suitable node to reside in, quite often, in fact more often than not, the majority of leaf nodes for each hierarchy are empty at any given moment.
    Therefore, although we always know the root sector of a given hierarchy and can traverse downwards from there, if we worked our way up from the intersection table, we could eliminate up to 70% of the nodes of any given hierarchy from further consideration, as they don't need to be (in fact, must not be) included in reports.
    As implied by the above, rows in M are structurally similar (in terms of columns, but not in any real world sense) and are a superset of rows in H. But combining them into the one table doesn't seem to help the reporting process due to the many-to-many relationship which prevents the ID/parentID relationship from being carried through to this level.
    There are a number of other considerations of which the most pertinent is that the people using this database generally have an interest in only a subset of the master list of items in M. This relationship is also dynamic but important enough and rigid enough that another link table P exists to combine the Users in table U with the subset of M in which they are interested. (The users are also grouped into hierarchies of a totally different nature, but this aspect is secondary for now.)
    The reporting is reasonably straightforward for any single combination of User and Hierarchy; they want to see all the items they are interested in, listed in hierarchical sequence, totalled on change of level with the individual items M listed beneath the nodes of H. This is unfortunately required in real time, so retrieval performance is paramount.
    Some statistics might help to determine the optimum approach:
    The largest hierarchy has 10,000 nodes. The smallest about 100.
    The largest would have 70% or more of its nodes unused at any point in time, and even the smallest would have 25% unused.
    The hierarchies tend to be broad rather than deep, the maximum number of levels being about 5; but the larger ones should be twice as deep as this if performance was not compromised.
    There are dozens of hierarchies, but it may be possible to sharply reduce this number by exploiting the Order Siblings By clause.
    The number of rows in M varies between 500,000 and 50,000; depending upon how long historical data is retained on-line (and performance permitting, it would be retained indefinitely).
    The number of users varies between 1000 and 2000 but the range of M in which they are interested varies greatly; from as few as 100 to as many as 10,000+. So it is almost always worth beginning by eliminating the items in which they are not interested, implying once again that the hierarchy should be traversed upwards rather than down from the root.
    The current system is very old and survives by a tactic of building what are essentially materialised views of the database structure for each user overnight using, ahem, non-relational technology. This is inefficient and not easily scaled (but it works) and hence this redevelopment project needs to (a) work, and (b) work better and faster.
    I am happy to provide some DDL scripts if that helps explain the problem better than this narrative.
    I can't help feeling that the solution lies in somehow extending the hierarchical query past the many-to-many link table so that the Master list can be merged directly into the hierarchy such that the M items become the leaf nodes rather than the design outlined above - but I don't know how to do that. But I am sure everyone reading this does! :)
    All advice appreciated. Database version is not an issue; we are currently using version 10XE for experimentation, but production usage could be on 11 if that contains helpful features.
    Thank you
    CS

    Hi,
    ChrisS. wrote:
    I am happy to provide some DDL scripts if that helps explain the problem better than this narrative.Yes, please do.
    The problem seems interesting, I'm sure many people here (including myself) are willing to help you in this matter.
    So yes, post DDL for the tables, as well as INSERTs to populate them with representative data. Please also include the output you require along with detailed explanations about the logic to get it.
    Don't forget to put lines of code between &#x007B;code&#x007D; tags in order to preserve formatting and readability, like this :
    SELECT sysdate FROM dual;Thanks.

  • START WITH and CONNECT BY in Oracle SQL ( hierarchical relationship)

    Hi, the original table as below
    Customer_ID         Account_ID          Paying_Account_ID         Parent_Account_ID          Company_ID
    158                    158                    158                         158                     0
    159                    159                    158                         158                     0
    160                    160                    158                         158                     0
    181                    181                    181                         181                     0
    183                    183                    183                         183                     0
    24669                  24669                  24669                       24669                   0        
    24671                  24671                  24671                       24669                   0
    24670                  24670                  24670                       24669                   0    
    3385127                3385127                3385127                     24670                   0To identify the hierarchical relationship of the data, which are PARENT_ACCOUNT_ID & ACCOUNT_ID, below is the query that I was used.
    select  lpad(' ', 2*level) || A.ACCOUNT_ID AS LEVEL_LABEL, CONNECT_BY_ISCYCLE "Cycle", LEVEL, A.* from ACCOUNT A
    START WITH parent_account_id = account_id
    CONNECT BY NOCYCLE  PRIOR A.ACCOUNT_ID = A.PARENT_ACCOUNT_ID
    AND account_id != parent_account_id
    ;This is the result from the query
    Level_Label              Level          Cycle        Customer_ID             Account_ID        Paying_Account_ID      Parent_Account_ID      Company_ID
    158                         1             0              158                     158              158                   158                     0
       159                      2             0              159                     159              158                   158                     0
       160                      2             0              160                     160              158                   158                     0
    181                         1             0              181                     181              181                   181                     0
    183                         1             0              183                     183              183                   183                     0
    24669                       1             0              24669                   24669            24669                 24669                   0      
        24671                   2             0              24671                   24671            24671                 24669                   0
        24670                   2             0              24670                   24670            24670                 24669                   0
            3385127             3             0              3385127                 3385127          3385127               24670                   0My questions is how can I modified the query in order to calcuate the values for:
    My_Total_PR - Number of my child PR accounts which doest not include itself.
    Total_PR - Total number of PR accounts in the overall structure
    My_Total_NPR - Number of my child NPR accounts which doest not include itself.
    Total_NPR - Total number of NPR accounts in the overall structure
    *PR stand for payment responsible, for instance the payment responsible for Account 158 is 158 (Paying_Account_ID), so the Total_PR for 158 is 3 (158, 159, 160)
    *NPR stand for Non payment responsible, for instance the payment responsible for Account 159 is 158 (Paying_Account_ID), so the Total_NPR for 159 is 1
    This is the expected result, Any advice much appreciated. Thanks
    Level_Label                     Level           Cycle           My_Total_PR     Total_PR     My_Total_NPR     Total_NPR     Paying_Account
    158                               1                0                  2              3          0              0              158
        159                           2                0                  0              0          0              1              158
        160                           2                0                  0              0          0              1              158
    181                               1                0                  0              1          0              0              181
    183                               1                0                  0              1          0              0              183
    24669                             1                0                  0              1          3              3              24669                  
        24671                         2                0                  0              1          0              0              24671
        24670                         2                0                  0              1          1              1              24670
            3385127                   3                0                  0              1          0              0              3385127Edited by: user11432758 on 14-Feb-2012 01:00
    Edited by: user11432758 on 14-Feb-2012 07:05

    Hi,
    user11432758 wrote:
    Hi below is the DDL statment, thanks
    CREATE TABLE "SYSTEM"."ACCOUNT" ...
    Don't create your own objects in the SYSTEM schema, or any schema that comes with the database. Create a separate schema, and put your objects into it. You'll have fewer security problems, and migrating to a new database will be easier.
    Here's one way to can get the aggregates that you want:
    WITH     got_descendants          AS
         SELECT     CONNECT_BY_ROOT account_id     AS ancestor_id
         ,     paying_account_id
         ,     LEVEL                    AS lvl
         FROM     account
         CONNECT BY NOCYCLE     PRIOR account_id     = parent_account_id
              AND          account_id          != parent_account_id
    SELECT       ancestor_id
    ,       COUNT (CASE WHEN lvl             > 1
                      AND  ancestor_id  = paying_account_id THEN 1 END)     AS my_total_pr
    ,       COUNT (CASE WHEN ancestor_id  = paying_account_id THEN 1 END)     AS total_pr
    ,       COUNT (CASE WHEN lvl             > 1
                      AND  ancestor_id != paying_account_id THEN 1 END)     AS my_total_npr
    ,       COUNT (CASE WHEN ancestor_id != paying_account_id THEN 1 END)     AS total_npr
    FROM       got_descendants
    GROUP BY  ancestor_id
    ;Output:
    `             MY_         MY_
                TOTAL TOTAL TOTAL TOTAL
    ANCESTOR_ID   _PR   _PR  _NPR  _NPR
            158     2     3     0     0
            159     0     0     0     1
            160     0     0     0     1
            181     0     1     0     0
            183     0     1     0     0
          24669     0     1     3     3
          24670     0     1     1     1
          24671     0     1     0     0
        3385217     0     1     0     0This gives you the right numbers, but how can we get them in an order that reflects the hierarchy, with the columns (such as lvl) that are derived from the hierarchy?
    One way would be to do two CONNECT BY queries; one without a START WITH clause (like the one above) that gets the aggregates, and the other with a START WITH clause (like your original query), that is in the right order, and has columns such as level_label and level. We could join the result sets and get exactly what we want. I'll leave that as an exercise.
    Here's another way, that gets the right results with only one CONNECT BY query:
    WITH     got_descendants          AS
         SELECT     CONNECT_BY_ROOT account_id     AS ancestor_id
         ,     paying_account_id
         ,     account_id
         ,     LEVEL                    AS lvl
         ,     CONNECT_BY_ISCYCLE          AS cycle
         ,     CASE
                  WHEN  CONNECT_BY_ROOT account_id
                      = CONNECT_BY_ROOT parent_account_id
                  THEN  ROWNUM
              END                    AS r_num
         FROM     account
         CONNECT BY NOCYCLE     PRIOR account_id     = parent_account_id
              AND          account_id          != parent_account_id
         ORDER SIBLINGS BY     account_id     -- Optional
    ,     got_o_num     AS
         SELECT     got_descendants.*
         ,     MIN (r_num) OVER (PARTITION BY  account_id)     AS o_num
         ,     MAX (lvl)   OVER (PARTITION BY  account_id)      AS max_lvl
         FROM     got_descendants
    SELECT       LPAD ( ' '
                , 2 * (MIN (max_lvl) - 1)
                )  || ancestor_id                         AS level_label
    ,       MIN (max_lvl)                                AS "Level"
    ,       MIN (cycle)                                   AS "Cycle"
    ,       COUNT (CASE WHEN lvl             > 1
                      AND  ancestor_id  = paying_account_id THEN 1 END)     AS my_total_pr
    ,       COUNT (CASE WHEN ancestor_id  = paying_account_id THEN 1 END)     AS total_pr
    ,       COUNT (CASE WHEN lvl             > 1
                      AND  ancestor_id != paying_account_id THEN 1 END)     AS my_total_npr
    ,       COUNT (CASE WHEN ancestor_id != paying_account_id THEN 1 END)     AS total_npr
    ,       MIN (paying_account_id)                                    AS paying_account
    FROM       got_o_num
    GROUP BY  ancestor_id
    ORDER BY  MIN (o_num)
    ;Output:
    `                             MY_         MY_
                                TOTAL TOTAL TOTAL TOTAL  PAYING_
    LEVEL_LABEL     Level Cycle   _PR   _PR  _NPR  _NPR  ACCOUNT
    158                 1     0     2     3     0     0      158
      159               2     0     0     0     0     1      158
      160               2     0     0     0     0     1      158
    181                 1     0     0     1     0     0      181
    183                 1     0     0     1     0     0      183
    24669               1     0     0     1     3     3    24669
      24670             2     0     0     1     1     1    24670
        3385217         3     0     0     1     0     0  3385217
      24671             2     0     0     1     0     0    24671This is exactly what you requested, except that you posted the row with level_label='  24671' before the row with level_label='  24671'. You may not care which of those comes first, but if that's important, explain why those rows need to be in descending order by account_id, while '159 and '160' are in ascending order. You'll need to change the ORDER SIBLINGS BY clause accordingly.

  • [10g] Need help with order by clause in hierarchical query

    I have the following sample data:
    CREATE TABLE     bill_test1
    (     parent_part     CHAR(25)
    ,     child_part     CHAR(25)
    ,     line_nbr     NUMBER(5)
    ,     qty_per          NUMBER(9,5)
    INSERT INTO bill_test1 VALUES ('ABC-1','ABC-10',100,1);
    INSERT INTO bill_test1 VALUES ('ABC-1','ABC-20',200,2);
    INSERT INTO bill_test1 VALUES ('ABC-1','ABC-30',300,3);
    INSERT INTO bill_test1 VALUES ('ABC-1','HARDWARE-1',401,10);
    INSERT INTO bill_test1 VALUES ('ABC-1','HARDWARE-2',402,5);
    INSERT INTO bill_test1 VALUES ('ABC-10','ABC-155',100,2);
    INSERT INTO bill_test1 VALUES ('ABC-10','HARDWARE-1',200,1);
    INSERT INTO bill_test1 VALUES ('ABC-155','RAW-2',100,4.8);
    INSERT INTO bill_test1 VALUES ('ABC-155','HARDWARE-3',200,3);
    INSERT INTO bill_test1 VALUES ('ABC-20','RAW-1',100,10.2);
    INSERT INTO bill_test1 VALUES ('ABC-30','RAW-3',100,3);And the query below gives me exactly what I want, in the order I want it. However, I am wondering if there is a way to get this order without creating the SEQ column, since I don't need it in my results
    SELECT     part_nbr
    ,     parent_part
    ,     child_part
    FROM     (
         SELECT     CONNECT_BY_ROOT b.parent_part                         AS part_nbr
         ,     b.parent_part
         ,     b.child_part
         ,     SYS_CONNECT_BY_PATH(b.line_nbr,' ')                    AS seq
         FROM     bill_test1 b
         ,     dual
         CONNECT BY     parent_part     = PRIOR child_part
    WHERE          part_nbr     = 'ABC-1'
    ORDER BY     seq
    Results of above query, except with SEQ included in SELECT (just to show what I'm sorting off of):
    PART_NBR                     PARENT_PART                  CHILD_PART                   SEQ
    ABC-1                        ABC-1                        ABC-10                        100
    ABC-1                        ABC-10                       ABC-155                       100 100
    ABC-1                        ABC-155                      RAW-2                         100 100 100
    ABC-1                        ABC-155                      HARDWARE-3                    100 100 200
    ABC-1                        ABC-10                       HARDWARE-1                    100 200
    ABC-1                        ABC-1                        ABC-20                        200
    ABC-1                        ABC-20                       RAW-1                         200 100
    ABC-1                        ABC-1                        ABC-30                        300
    ABC-1                        ABC-30                       RAW-3                         300 100
    ABC-1                        ABC-1                        HARDWARE-1                    401
    ABC-1                        ABC-1                        HARDWARE-2                    402

    Hi,
    As long as there's only one root, you can say ORDER SIBLINGS BY, but you can't do that in a sub-query (well, you can, but usually there's no point in doing it in a sub-query). If the CONNECT BY is being done in a sub-query, there is no guarantee that the main query will preserve the hierarchical order that the sub-query provides.
    The query you posted doesn't require a suib-query, so you can say:
    SELECT     CONNECT_BY_ROOT b.parent_part                         AS part_nbr
    ,     b.parent_part
    ,     b.child_part
    --,     SYS_CONNECT_BY_PATH(b.line_nbr,' ')                    AS seq
    FROM     bill_test1 b
    WHERE          CONNECT_BY_ROOT b.parent_part     = 'ABC-1'
    CONNECT BY     parent_part     = PRIOR child_part
    ORDER SIBLINGS BY     b.line_nbr     
    ;I said the query you posted doesn't require a sub-query. It also doesn't require dual, so I suspect what you posted is a simplification of what you're really doing, and that may need a sub-query. In particular, if you intend to GROUP BY part_nbr, then you need the sub-query. We can repeat the CONNECT_BY_ROOT expression in the WHERE clause (or, now that I think about it, use a START WITH clause instead of WHERE), but, for some reason, we can't use CONNECT_BY_ROOT in a GROUP BY clause; we need to compute CONNECT_BY_ROOT in a sub-query, give it a name (like part_nbr), and GROUP BY that column in a super-query.
    This assumes that there is only one root node. ORDER SIBLINGS BY means just that: children of a common parent will appear in order, but the root nodes, who have no parents, will not necessarily be in order.
    Here's what I meant by using START WITH instead of WHERE:
    SELECT     CONNECT_BY_ROOT b.parent_part                         AS part_nbr
    ,     b.parent_part
    ,     b.child_part
    --,     SYS_CONNECT_BY_PATH(b.line_nbr,' ')                    AS seq
    FROM     bill_test1 b
    START WITH     b.parent_part     = 'ABC-1'
    CONNECT BY     parent_part     = PRIOR child_part
    ORDER SIBLINGS BY     b.line_nbr     
    ;This should be much more efficient, because it narrows down the results before you waste time getting their descendants.
    Using a START WITH clause here is analagous to me sending you an e-mail, saying "Come to a meeting a my office at 3:00."
    Using a WHERE clause here is analagous to me sending an e-mail to everyone in the company, saying "Come to a meeting a my office at 3:00", and then, as people get here, telling everyone except you that they can go back.
    ORDER SIBLINGS BY was introduced in Oracle 9.
    Edited by: Frank Kulash on Dec 9, 2010 2:39 PM
    Added version with START WITH clause

  • How to use order by with hierarchical query

    I have a hierarchical query basically it brings back an organization chart. We start with a manager's id, get all that person's employees. If any of the employees is also a manager I want to get that person's employees and return them right after that person. I won't bother with the whole query but relevant part is:
           START WITH em.mgr_id = pi_mgr_id
          CONNECT BY nocycle PRIOR em.emp_id = em.mgr_id;Where pi_mgr_id is a parameter passed to the procedure and em is the alias for the emp_mgr_relationship table which contains emp_id and mgr_id. This works fine. What I want now is for the employees who work for the same manager to appear in name order. The table which contains the employee names is aliased as pe and the name column is called name1. I added the following:
           START WITH em.mgr_id = pi_mgr_id
          CONNECT BY nocycle PRIOR em.emp_id = em.mgr_id
            order by pe.name1;But that put the entire list in name order. What I want is for employees who work for the same manager to be in name order. Let's the manager whose organization I want is named Frank. What I'd like to get is this
    EMP_NAME    MGR_NAME
    Allen       Frank
    Beth        Frank
    Alex        Beth
    Charles     Beth
    Ed          Beth
    Dean        Frank
    George      Frank
    Benny       George
    David       George
    Sam         George
    Dan         Sam
    Harry       Sam
    John        Sam
    Terry       George
    James       Frank
    Ken         Frank
    Mike        Ken
    Warren      KenHow do I get the list in this order?
    Edited by: kendenny on Jul 28, 2010 7:31 AM

    Make use of ORDER SIBLINGS clause in hierarchial queries to set the order by child columns.
    START WITH em.mgr_id = pi_mgr_id
          CONNECT BY nocycle PRIOR em.emp_id = em.mgr_id
            *order siblings by name1;*

  • Problem with performance of a query having order by, distinct clause

    Hi,
    I have a problem with queries having order by, distinct clause.
    While its executing its taking lot of time. With DBMS_PROFILER identified the queries taking long time.
    The table is having approximately 70 million rows.
    Problem -1
    select * from table_name order by col1;
    select distinct col1,col2 from table_name;
    Here i am having 2 solutions request to let me know whether i am right if not suggest me right solution.
    Solution1:
    Max parallel servers is 8.
    select /* + parallel(table_name,8) */ * from table_name order by col1;
    select /* + parallel(table_name,8) */ distinct col1, col2 from table_name ;
    Solution-2:
    select /* + first_rows */ * from table_name order by col1;
    select /* + first_rows */ distinct col1, col2 from table_name ;
    Problem-2
    I am having a query with where condition on columns.
    Select * from table_name where col1='value1' and col2!='value2';
    Index created on col1 and col2.
    As we no that not equal won't use index as it is a composite index it should use the lead column. but its not using the index.
    Should i forcibly use index with hint or suggest me better solution.
    Any help really appreciated.
    Thanks in advance

    unique wrote:
    The table is having approximately 70 million rows.
    select * from table_name order by col1;Do you really want 70,000,000 rows from your table without any restrictions ? And furthermore ordered output ? I honestly understand the slowness of that query.
    Here i am having 2 solutions request to let me know whether i am right if not suggest me right solution.Who knows if you choosed the right solution. I would suggest to reconsider your query and the need of 70,000,000 returned rows.
    Problem-2
    I am having a query with where condition on columns.
    Select * from table_name where col1='value1' and col2!='value2';Please, provide the explain plan, eventually the tkprof output could also help. And tables descirption AND indexes description.
    And OS and Oracle version.
    Nicolas.

  • [Oracle 8i] Need help pruning branches from a hierarchical query

    My problem is that my hierarchical query seems only to trim out the values that don't meet my criteria, but still includes their children. When my query hits a record that does not meet my criteria, I want it to stop there. I've tried including the criteria in just the 'where' clause of the query, and have also put the criteria in the 'connect by' clause as well, but nothing has fixed it. Please keep in mind I'm using Oracle 8i, so I can't use some of the 'nicer' statements for hierarchical queries that they introduced in 9. I'm stuck with 'Start With...Connect By'.
    I have sample tables/data that I can post if someone needs to see that to help me, but to start with, here's my current query:
    SELECT     *
    FROM     (
              SELECT
                   LEVEL
              ,     c_bill.comp_part_nbr                     AS     c_part_nbr
              ,     (select c_part.part_desc
                   FROM part c_part
                   WHERE c_part.part_nbr=c_bill.comp_part_nbr)     AS     c_part_desc
              ,     (SELECT c_part.part_type
                   FROM part c_part
                   WHERE c_part.part_nbr=c_bill.comp_part_nbr)      AS     c_part_type
              ,     c_bill.qty_per                          AS     c_qty_per_p
              ,     c_bill.qty_per_type                     AS     c_qty_per_type
              ,     (SELECT c_part.qty_on_hand                
                   FROM part c_part
                   WHERE c_part.part_nbr=c_bill.comp_part_nbr)      AS     c_qty_on_hand
              ,     c_bill.oper_nbr                     AS     rqd_at_op
              ,     c_bill.comp_off_adj                     AS     rqd_offset
              ,     c_bill.bom_doc_nbr                     AS     p_part_nbr
              ,     (SELECT p_part.qty_on_hand
                   FROM part p_part
                   WHERE p_part.part_nbr=c_bill.bom_doc_nbr)      AS     p_qty_on_hand
              FROM
                   BILL c_bill
              WHERE
                             (c_bill.status           =      'RL')           
                        AND     (c_bill.view_code     IN      ('M','G'))     
                        AND     (c_bill.end_eff_dt     >      SYSDATE)      
                        AND     (c_bill.begn_eff_dt     <=      SYSDATE)
              START WITH c_bill.bom_doc_nbr=RPAD(?,25)
              CONNECT BY PRIOR c_bill.comp_part_nbr=c_bill.bom_doc_nbr
              AND     c_bill.view_code     IN     ('M','G')     
              AND     c_bill.status          =     'RL'
              AND      c_bill.end_eff_dt     >     SYSDATE
              AND     c_bill.begn_eff_dt     <=     SYSDATE     
         ) a
    WHERE     c_part_type = 'M'

    The outside criterion of part_type='M' isn't my problem. Where I'm actually seeing my issue rear its ugly head is in the criterion:
    (c_bill.view_code     IN      ('M','G'))What I'll have happen is that one of the children or grandchildren of the part number I'm querying for (my parameter), will be of some view code that's not 'M' or 'G'. In my sample data below, I have a level 4 part that is part of the 'H' view code, which I don't want, nor do I want it's children. However, its child is in the 'G' view code, and my query returns it anyway.
    In my sample data below, I'm assuming that the parameter = 'XYZ-100'
    CREATE TABLE part
    part_nbr     varchar(25) not null,
    part_desc     varchar(25) not null,
    part_type     char(1) not null,
    qty_on_hand     double(13,4) not null
    CONSTRAINT part_pk
    PRIMARY KEY (part_nbr),
    CONSTRAINT check_part_type
    CHECK (part_type IN ('M','P','X','Y')),
    CONSTRAINT check_qty_on_hand
    CHECK (qty_on_hand >= 0)
    CREATE TABLE bill
    row_added_ts     char(20) not null,
    bom_doc_nbr     varchar(25) not null,
    comp_part_nbr     varchar(25) not null,
    qty_per          double(9,5) not null,
    qty_per_type     char(1) not null,
    oper_nbr     char(4) not null,
    comp_off_adj     double(3,0),
    status          char(2),
    view_code     char(1) not null,
    end_eff_dt     date() not null,
    begn_eff_dt     date() not null
    CONSTRAINT bill_pk
    PRIMARY KEY (row_added_ts),
    CONSTRAINT check_qty_per_type
    CHECK (qty_per_type IN ('0','1','2','3')),
    CONSTRAINT check_status
    CHECK (status IN ('IN', 'RL')),
    );     Values for those tables:
    INSERT INTO part
    VALUES ('xyz-1', 'purchased part', 'P', 5);
    INSERT INTO part
    VALUES ('xyz-2', 'purchased part', 'P', 1);
    INSERT INTO part
    VALUES ('xyz-3', 'purchased part', 'P', 1);
    INSERT INTO part
    VALUES ('xyz-3a', 'manufactured part', 'M', 1);
    INSERT INTO part
    VALUES ('xyz-4', 'purchased part', 'P', 1);
    INSERT INTO part
    VALUES ('xyz-9-1', 'manufactured part', 'M', 0);
    INSERT INTO part
    VALUES ('xyz-9a', 'manufactured part', 'M', 0);
    INSERT INTO part
    VALUES ('raw-1', 'purchased raw material', 'P', 212);
    INSERT INTO part
    VALUES ('raw-2', 'purchased raw material', 'P', 75.5);
    INSERT INTO part
    VALUES ('XYZ-100', 'manufactured part', 'M', 0);
    INSERT INTO part
    VALUES ('(OPEN)', '(not in use)', 'Y', 0);
    INSERT INTO part
    VALUES ('XYZ-100-1', 'manufactured part', 'M', 0);
    INSERT INTO part
    VALUES ('XYZ-100-2', 'manufactured part', 'M', 1);
    INSERT INTO part
    VALUES ('XYZ-100-3', 'manufactured part', 'M', 0);
    INSERT INTO part
    VALUES ('XYZ-100-4', 'manufactured part', 'M', 2);
    INSERT INTO part
    VALUES ('XYZ-100-A', 'manufactured part', 'M', 0);
    INSERT INTO bill
    VALUES ('2008072153100150000','XYZ-100','xyz-1',3,'1','****',0,'RL','M','01-Jan-2050','01-Jan-1900');
    INSERT INTO bill
    VALUES ('2008072153100150000','XYZ-100','XYZ-100-1',1,'1','****',0,'RL','M','01-Jan-2050','01-Jan-1900');
    INSERT INTO bill
    VALUES ('2008072153100150000','XYZ-100-1','xyz-1',2,'1','****',1,'RL','M','01-Jan-2050','01-Jan-1900');
    INSERT INTO bill
    VALUES ('2008072153100150000','XYZ-100-1','XYZ-100-2',3,'1','****',0,'RL','M','01-Jan-2050','01-Jan-1900');
    INSERT INTO bill
    VALUES ('2008072153100150000','XYZ-100-2','xyz-2',6,'1','****',2,'RL','M','01-Jan-2050','01-Jan-1900');
    INSERT INTO bill
    VALUES ('2008072153100150000','XYZ-100-2','xyz-4',6,'1','****',2,'IN','M','01-Jan-2050','01-Jan-1900');
    INSERT INTO bill
    VALUES ('2008072153100150000','XYZ-100-2','xyz-100-3',1,'1','****',0,'RL','M','01-Jan-2050','01-Jan-1900');
    INSERT INTO bill
    VALUES ('2008072153100150000','XYZ-100-3','xyz-3',8,'1','****',1,'RL','M','01-Jan-2050','01-Jan-2000');
    INSERT INTO bill
    VALUES ('2008072153100150000','XYZ-100-3','xyz-3a',8,'1','****',1,'RL','M','01-Jan-2000','01-Jan-1900');
    INSERT INTO bill
    VALUES ('2008072153100150000','XYZ-100-3','XYZ-100-4',4,'1','****',0,'RL','M','01-Jan-2050','01-Jan-1900');
    INSERT INTO bill
    VALUES ('2008072153100150000','XYZ-100-3','XYZ-100-A',2,'1','****',2,'RL','M','01-Jan-2050','01-Jan-1900');
    INSERT INTO bill
    VALUES ('2008071153100150000','XYZ-100-3','(OPEN)',2,'1','****',0,'RL','E','01-Jan-2050','01-Jan-1900');
    INSERT INTO bill
    VALUES ('2008071153100150000','XYZ-100-3','xyz-9-1',2,'1','****',0,'RL','H','01-Jan-2050','01-Jan-1900');
    INSERT INTO bill
    VALUES ('2008072153100150000','XYZ-100-4','raw-1',8.75,'1','****',0,'RL','M','01-Jan-2050','01-Jan-1900');
    INSERT INTO bill
    VALUES ('2008072153100150000','XYZ-100-A','raw-2',3.75,'1','****',0,'RL','M','01-Jan-2050','01-Jan-1900');
    INSERT INTO bill
    VALUES ('2008075911100150000','xyz-9-1','xyz-9a',1,'1','****',0,'RL','G','01-Jan-2050','01-Jan-1900');
    INSERT INTO bill
    VALUES ('2008087711100150000','xyz-9a','raw-2',3.75,'1','****',0,'RL','G','01-Jan-2050','01-Jan-1900');Sample data displayed in table format:
    --PART table (from insert statements above)
    part_nbr     part_desc          part_type     qty_on_hand
    xyz-1           purchased part          P          5
    xyz-2           purchased part          P          1
    xyz-3           purchased part          P          1
    xyz-3a           manufactured part     M          1
    xyz-4           purchased part          P          1
    xyz-9-1           manufactured part     M          0
    xyz-9a           manufactured part     M          0
    raw-1           purchased raw material     P          212
    raw-2           purchased raw material     P          75.5
    XYZ-100           manufactured part     M          0
    (OPEN)          (not in use)          Y          0
    XYZ-100-1     manufactured part     M          0
    XYZ-100-2     manufactured part     M          1
    XYZ-100-3     manufactured part     M          0
    XYZ-100-4     manufactured part     M          2
    XYZ-100-A     manufactured part     M          0
    --BILL table (from insert statements above)
    row_added_ts          bom_doc_nbr     comp_part_nbr     qty_per     qty_per_type     oper_nbr     comp_off_adj     status     view_code     end_eff_dt     begn_eff_dt
    2008072153100150000     XYZ-100          xyz-1          3     1          ****          0          RL     G          01-Jan-2050     01-Jan-1900
    2008072223100150000     XYZ-100          XYZ-100-1     1     1          ****          0          RL     M          01-Jan-2050     01-Jan-1900
    2008072411100150000     XYZ-100-1     xyz-1          2     1          ****          1          RL     M          01-Jan-2050     01-Jan-1900
    2008072459100150000     XYZ-100-1     XYZ-100-2     3     1          ****          0          RL     M          01-Jan-2050     01-Jan-1900
    2008072578100150000     XYZ-100-2     xyz-2          6     1          ****          2          RL     M          01-Jan-2050     01-Jan-1900
    2008072694100150000     XYZ-100-2     xyz-4          6     1          ****          2          IN     G          01-Jan-2050     01-Jan-1900
    2008072786100150000     XYZ-100-2     xyz-100-3     1     1          ****          0          RL     M          01-Jan-2050     01-Jan-1900
    2008072865100150000     XYZ-100-3     xyz-3          8     1          ****          1          RL     M          01-Jan-2050     01-Jan-2000
    2008073100100150000     XYZ-100-3     xyz-3a          8     1          ****          1          RL     M          01-Jan-2000     01-Jan-1900
    2008073159100150000     XYZ-100-3     XYZ-100-4     4     1          ****          0          RL     M          01-Jan-2050     01-Jan-1900
    2008073346100150000     XYZ-100-3     XYZ-100-A     2     1          ****          2          RL     M          01-Jan-2050     01-Jan-1900
    2008073478100150000     XYZ-100-3     (OPEN)          2     1          ****          0          RL     E          01-Jan-2050     01-Jan-1900
    2008073529100150000     XYZ-100-3     xyz-9-1          2     1          ****          0          RL     H          01-Jan-2050     01-Jan-1900
    2008073798100150000     XYZ-100-4     raw-1          8.75     1          ****          0          RL     M          01-Jan-2050     01-Jan-1900
    2008073811100150000     XYZ-100-A     raw-2          3.75     1          ****          0          RL     M          01-Jan-2050     01-Jan-1900
    2008075911100150000     xyz-9-1          xyz-9a          1     1          ****          0          RL     G          01-Jan-2050     01-Jan-1900
    2008087711100150000     xyz-9a          raw-2          3.75     1          ****          0          RL     G          01-Jan-2050     01-Jan-1900--What I want to get with my query (branches pruned off my tree)
    LEVEL     C_PART_NBR     C_PART_DESC          C_PART_TYPE     C_QTY_PER_P     C_QTY_PER_TYPE     C_QTY_ON_HAND     RQD_AT_OP     RQD_OFFSET     P_PART_NBR     P_QTY_ON_HAND
    1     XYZ-100-1     manufactured part     M          1          1          0          ****          0          XYZ-100          0
    2     XYZ-100-2     manufactured part     M          3          1          1          ****          0          XYZ-100-1     0
    3     xyz-100-3     manufactured part     M          1          1          0          ****          0          XYZ-100-2     1
    4     XYZ-100-4     manufactured part     M          4          1          2          ****          0          XYZ-100-3     0
    4     XYZ-100-A     manufactured part     M          2          1          0          ****          2          XYZ-100-3     0--What I actually get with my query (includes children of items that don't meet query criteria)
    LEVEL     C_PART_NBR     C_PART_DESC          C_PART_TYPE     C_QTY_PER_P     C_QTY_PER_TYPE     C_QTY_ON_HAND     RQD_AT_OP     RQD_OFFSET     P_PART_NBR     P_QTY_ON_HAND
    1     XYZ-100-1     manufactured part     M          1          1          0          ****          0          XYZ-100          0
    2     XYZ-100-2     manufactured part     M          3          1          1          ****          0          XYZ-100-1     0
    3     xyz-100-3     manufactured part     M          1          1          0          ****          0          XYZ-100-2     1
    4     XYZ-100-4     manufactured part     M          4          1          2          ****          0          XYZ-100-3     0
    4     XYZ-100-A     manufactured part     M          2          1          0          ****          2          XYZ-100-3     0
    5     xyz-9a          manufactured part     M          1          1          0          ****          0          xyz-9-1          0Edited by: user11033437 on Jul 30, 2009 7:27 AM (grammar)

  • Hierarchical query with where clause

    Hi,
    How can I query hierarchically a query with WHERE clause? I have a table with three fields session_id,id and root_id.
    When I try with the following query,
    select id, level from relation
    where session_id = 79977
    connect by prior id = root_id start with id = 5042;
    It gets duplicate values.
    I want the query to show in the hierarchical manner with a filter condition using WHERE clause. Please help me how can I achieve this. If you know any link that describes more about this, please send it.
    Thanks in Advance.
    Regards,
    -Parmy

    Hi Sridhar Murthy an others,
    Thanks a lot for your/the answer. It's working for me. It saved a lot of other work around without the proper knowledge of hierarchical query. Please send me any link that describes these issues in detail and also I hope as I have mentioned in the other message, same cannot be achieved on views or ( on two different tables ???)
    Any way thanks for your reply,
    It's working for me.
    With happiness,
    -Parmy

  • How to convert SQL Server hierarchical query (CTE) to Oracle?

    How to convert SQL Server hierarchical query (CTE) to Oracle?
    WITH cte (col1, col2) AS
    SELECT col1, col2
    FROM dbo.[tb1]
    WHERE col1 = 12
    UNION ALL
    SELECT c.col1, c.col2
    FROM dbo.[tb1] AS c INNER JOIN cte AS p ON c.col2 = p.col1
    DELETE a
    FROM dbo.[tb1] AS a INNER JOIN cte AS b
    ON a.col1 = b.col1

    See: http://download.oracle.com/docs/cd/B19306_01/server.102/b14200/statements_10002.htm#i2129904
    Ah, just spotted it's a delete statement - you won't be able to do it using subquery factoring in Oracle.
    I had a go at trying to convert for you, but notice that you reference the cte from inside the cte... I can only assume you have a table called cte too...
    DELETE FROM dbo.tb1
    WHERE col1 = 12
    OR col2 IN (SELECT col1 FROM cte)
    Edited by: Boneist on 22-Jun-2009 09:19

  • Too many results in hierarchically query

    Hello all,
    I'm searching for an idea to stop getting results 2, 3 and more times out of the following query
    select
    t.lvl,
    t.syswflvl,
    t.upper,
    LPAD(' ', (lvl)*8)||t.code code,
    LPAD(' ', (lvl)*8)||t.bezeichnung bezeichnung,
    t.chk,
    t.rang
    from (
    select '0' lvl, '0-'||to_char(syswftable,'0000000') syswflvl, '0- 0000000' upper, syscode code, bezeichnung, '' chk, 0 rang from wftable where (select count(wfm.syswftable) from wfm where wfm.syswftable = wftable.syswftable) > 0 union
    select '1' lvl, '1-'||to_char(syswfm,'0000000') syswflvl, '0-'||to_char(syswftable,'0000000') upper, syscode code, kurzbez bezeichnung, anzeigefilter chk, 1 rang from wfm union
    select '2' lvl, '2-'||to_char(syswfa,'0000000') syswflvl, '1-'||to_char(syswfm,'0000000') upper, syscode code, kurzbez bezeichnung, bedingung chk, rang from wfa union
    select '3' lvl, '3-'||to_char(syswfc,'0000000') syswflvl, '2-'||to_char(syswfa,'0000000') upper, syscode code, kurzbez bezeichnung, bedingung chk, rang from wfc union
    select '4' lvl, '4-'||to_char(syswfg,'0000000') syswflvl, '3-'||to_char(syswfc,'0000000') upper, syscode code, kurzbez bezeichnung, bedingung chk, rang from wfg
    ) t
    where ((t.chk not like '%and 0%'
    and trim(t.chk) not like '0%')
    or t.chk is null)
    and upper not like '%-'
    connect by nocycle prior syswflvl = upper
    order siblings by upper, syswflvl, rang
    What happens is, that I get the results from level 0 one times, from level 1 two times, from level 2 three times etc.
    What I'm try to achive is to get only the whole thing once.
    Hope you can see what my problem is ;-)
    Regards
    Carsten

    The effect of not having a start with clause in a hierarchical query is that a hierarchy is produced starting at every possible entry-point. So, you'll get the hierachy from node 1, the hierarchy from all nodes 2, the hierarchy from all nodes 3 and so one. Whether it starts with the correct one or not is not really relevant, as you already figured out that you get too much....
    So, just believe it, add a start with clause...and your problems are gone.

  • Hierarchical Query with Rollup Sum (CONNECT BY with GROUP BY ROLLUP)

    Hi all,
    Imagine the following scenario: i have an ACCOUNT table which holds accounts and their hierarchy (currently 5 levels), and a BALANCE table which holds balance records for the accounts. Only CHILD accounts (level 5) have records in the BALANCE table. Simple example:
    CREATE TABLE accounts (account_code VARCHAR2(30), parent_account VARCHAR2(30), account_desc VARCHAR2(400));
    CREATE TABLE balances (account_code VARCHAR2(30), balance_amount NUMBER(18,2));
    INSERT INTO ACCOUNTS VALUES ('TOT',NULL,'Total');
    INSERT INTO ACCOUNTS VALUES ('ANA1','TOT','General Expenses');
    INSERT INTO ACCOUNTS VALUES ('4801001','ANA1','Small Expenses');
    INSERT INTO ACCOUNTS VALUES ('4801002','ANA1','Transportation');
    INSERT INTO ACCOUNTS VALUES ('ANA2','TOT','Health Expenses');
    INSERT INTO ACCOUNTS VALUES ('4802001','ANA2','Healthcare');
    INSERT INTO ACCOUNTS VALUES ('4802002','ANA2','Facilities');
    INSERT INTO BALANCES VALUES ('4801001', 2000);
    INSERT INTO BALANCES VALUES ('4801002', 1000);
    INSERT INTO BALANCES VALUES ('4802001', 3000);
    INSERT INTO BALANCES VALUES ('4802002', 4000);What i need in this scenario is to run a hierarchical query, where for each node i compute the sum of all its children (In LEAF nodes which are the child accounts, this sum is the value in BALANCES itself). Final Result would be:
    TOT -> 10000
      ANA1 -> 3000
        4801001 -> 2000
        4801001 -> 1000
      ANA2 -> 7000
        4802001 -> 3000
        4802002 -> 4000I have tried various ways, and found out a workaround which works for a fixed amount of levels, basically it builds the hierarchy and computes the SYS_CONNECT_BY_PATH, then splits this as a regular expression and uses GROUP BY ROLLUP to compute the higher levels. Then i assemble it again, now with the computed values. Below is the example query:
    select level
        , NVL (vfinal.child_account,'TOTAL') ||' - '||
                            ( SELECT account_desc
                                FROM accounts
                               WHERE account_code = vfinal.child_acct ) account_name
         , to_char(sum_bal, 'fm999g999g999g990') as rolled_up_balance
      from
    select coalesce( princ.lvl3, princ.lvl2, princ.lvl1 ) child_acct
         , DECODE ( princ.lvl2 , NULL
                                     , NULL
                                     , DECODE ( princ.conta_lvl3, NULL
                                     , princ.conta_lvl1,princ.conta_lvl2 ) ) parent_acct
         , sum(princ.balance_amount) sum_bal
    from (
    select hier.lvl1
         , hier.lvl2
         , hier.lvl3
         , hier.parent_account
         , hier.account_code child_acc
         , bal.balance_amount
      from ( select level 
                  , sys_connect_by_path( account_code, '/' ) hierarchy_acct
                  , REGEXP_SUBSTR(sys_connect_by_path( account_code, '/' ),'[^/]+',1,3) lvl3
                  , REGEXP_SUBSTR(sys_connect_by_path( account_code, '/' ),'[^/]+',1,2) lvl2
                  , REGEXP_SUBSTR(sys_connect_by_path( account_code, '/' ),'[^/]+',1,1) lvl1
                  , account_code
                  , parent_account 
               from accounts acc
               where level <= 3
               start with parent_account is null
               connect by nocycle prior account = parent_account
               order siblings by parent_account
               ) hier
          , balances  bal
      where bal.cod_conta  = hier.account_code
    ) princ
    where princ.lvl1 is not null
    group by rollup ( princ.lvl1
                    , princ.lvl2
                    , princ.lvl3 )
    order by princ.conta_lvl1
           , princ.conta_lvl2
           , princ.conta_lvl3
    ) vfinal
    where child_acct is not null
    start with parent_acct is null
    connect by nocycle prior child_acct = parent_acctAll said and done, what i need is to do the same thing for infinite levels, because this query has 3 fixed levels. Do you know how can i structure a new query where, independently of the number of levels, the parent sums are all rolled up like this?
    Thanks a lot in advance! Best Regards!
    Thiago
    Edited by: Thiago on Sep 6, 2011 11:31 AM
    Edited by: Thiago on Sep 6, 2011 1:01 PM

    Hi,
    Thiago wrote:
    Hi all,
    Imagine the following scenario: i have an ACCOUNT table which holds accounts and their hierarchy (currently 5 levels), and a BALANCE table which holds balance records for the accounts. Only CHILD accounts (level 5) have records in the BALANCE table. Simple example:
    CREATE TABLE accounts (account_code VARCHAR2(30), parent_account VARCHAR2(30), account_desc VARCHAR2(400));
    CREATE TABLE balances (account_code VARCHAR2(30), balance_amount NUMBER(18,2));
    INSERT INTO ACCOUNTS ('TOT',NULL,'Total');
    INSERT INTO ACCOUNTS ('ANA1','TOT','General Expenses');
    INSERT INTO ACCOUNTS ('4801001','ANA1','Small Expenses');
    INSERT INTO ACCOUNTS ('4801002','ANA1','Transportation');
    INSERT INTO ACCOUNTS ('ANA2','TOT','Health Expenses');
    INSERT INTO ACCOUNTS ('4802001','ANA2','Healthcare');
    INSERT INTO ACCOUNTS ('4802002','ANA2','Facilities');
    INSERT INTO BALANCES ('4801001', 2000);
    INSERT INTO BALANCES ('4801001', 1000);
    INSERT INTO BALANCES ('4802001', 3000);
    INSERT INTO BALANCES ('4802001', 4000);
    Thanks for posting the CREATE TABLE and INSERT statements. Remember why you do it: so that the people who want to help you can re-create the problem and test their ideas. If the statments don't work, then they are not so useful. None of the INSERT statements you posted work: they all need a VALUES keyword. Please test those statments before you post them.
    Also, make sure that the reuslts you post correspond to the sample data you post. In your sample data, there are no rows in balances for account_codes '4801002' or '4802002'.
    I think you want something like this:
    WITH  connect_by_results      AS
         SELECT     CONNECT_BY_ROOT account_code     AS root_account_code
         ,     account_code
         FROM     accounts
                             -- NOTE: No START WITH clause
         CONNECT BY     parent_account     = PRIOR account_code
    SELECT       c.root_account_code     || ' -> '
                          || TO_CHAR (SUM (b.balance_amount))     AS txt
    FROM           connect_by_results  c
    LEFT OUTER JOIN      balances          b  ON  c.account_code = b.account_code
    GROUP BY  c.root_account_code
    ;

  • Hierarchical query with out using Connect by prior

    Hi Guys,
    I am supporting a product which is enterprise based and only allowd to write queries which are ANSII standard.
    I have an requirement like If I provide the child I need to know all the parents till  highest level.
    My table structure is like below
    Table_name :  Org_unit
    Columns are
    Org_unit_id name desc  parent_org_unit_id
    I wil pass the org_unit id and want to list all the parents of the chile org_unit_id and it has to be accomplished without  using connec by prior.
    Please suggest me some ideas and aprroches
    I am using Orcle 11g version

    Hi,
    960593 wrote:
    Hi Guys,
    I am supporting a product which is enterprise based and only allowd to write queries which are ANSII standard.
    I have an requirement like If I provide the child I need to know all the parents till  highest level.
    My table structure is like below
    Table_name :  Org_unit
    Columns are
    Org_unit_id name desc  parent_org_unit_id
    I wil pass the org_unit id and want to list all the parents of the chile org_unit_id and it has to be accomplished without  using connec by prior.
    Please suggest me some ideas and aprroches
    I am using Orcle 11g version
    The data model you posted (org_unit_id as primary key, parent_org_unit_id as foreign key to the same table for the parent, when there is a parent) is called the Adjacency Model because it keeps track of which nodes are adjacent (or next to) each other.
    I'm familiar with 2 other ways to model hierarchies: the Nested Sets Model, and what I call the Lineage Model.  I'll show how to find a given node's ancestors (in hierarchical order) in each model.  Neither the Nested Sets nor the Lineage Model requires CONNECT BY or recursive WITH clauses to work.
    The following table contains all the columns necessary for using each of these 3 models:
         EMPNO        MGR ENAME      LINEAGE                    NS_LOW NS_HIGH
          7839            KING       /7839/                          1      28
          7698       7839 BLAKE      /7839/7698/                     2      13
          7499       7698 ALLEN      /7839/7698/7499/                3       4
          7900       7698 JAMES      /7839/7698/7900/                5       6
          7654       7698 MARTIN     /7839/7698/7654/                7       8
          7844       7698 TURNER     /7839/7698/7844/                9      10
          7521       7698 WARD       /7839/7698/7521/               11      12
          7782       7839 CLARK      /7839/7782/                    14      17
          7934       7782 MILLER     /7839/7782/7934/               15      16
          7566       7839 JONES      /7839/7566/                    18      27
          7902       7566 FORD       /7839/7566/7902/               19      22
          7369       7902 SMITH      /7839/7566/7902/7369/          20      21
          7788       7566 SCOTT      /7839/7566/7788/               23      26
          7876       7788 ADAMS      /7839/7566/7788/7876/          24      25
    The Lineage Model keeps track of all of a given nodes ancestors, so if all you need to find are the primary keys of a given node, it's really trivial: it's all in the lineage column.  If you want to find more information about those ancestors, then you can do a self-join, like this:
    SELECT    a.empno, a.ename, a.lineage
    FROM      emp  a
    JOIN      emp  d   ON  d.lineage  LIKE '%/' || a.empno || '/%'
    WHERE     d.ename  IN ('ADAMS')
    ORDER BY  d.ename
    ,         a.lineage
    Output:
         EMPNO ENAME      LINEAGE
          7839 KING       /7839/
          7566 JONES      /7839/7566/
          7788 SCOTT      /7839/7566/7788/
          7876 ADAMS      /7839/7566/7788/7876/
    The Nested Sets model is harder to understand.
    Imagine everyone in the hierarchy standing on a wide staircase, as if for a group picture; everyone on the same level standing on the same step.  Everyone is holding up an umbrella that is wide enough to cover himself and all the people who are under him in the hierarchy.  The people with no underlings have small umbrellas, denoted like this "<-SMITH->", and peole that manage others have bigger umbrellas, like this: <-------- JONES -------->.  So the group picture might look like this:
    <-------------------------------------------- KING --------------------------------------------->
      <---------------------- BLAKE --------------------->  <-- CLARK -->  <-------- JONES -------->
       <-ALLEN-> <-JAMES-> <-MARTIN-> <-TURNER-> <-WARD->    <-MILLER->     <-- FORD--> <--SCOTT-->
                                                                             <-SMITH->   <-ADAMS->
    Each parent's umbrella covers all of his descendants (children, grandchildren, etc.), and nobody else.
    Now draw vertical lines trom the edges of each umbrella downwards, and number those lines from left to right:
    <-------------------------------------------- KING ------------------------------------------------>
    |                                                                                                  |
    | <---------------------- BLAKE --------------------->  <-- CLARK ->   <-------- JONES ----------> |
    | |                                                  |  |          |   |                         | |
    | |<-ALLEN-> <-JAMES-> <-MARTIN-> <-TURNER-> <-WARD->|  |<-MILLER->|   |<-- FORD--> <--SCOTT---> | |
    | ||       | |       | |        | |        | |      ||  ||        ||   ||         | |          | | |
    | ||       | |       | |        | |        | |      ||  ||        ||   ||<-SMITH->| |<-ADAMS-> | | |
    | ||       | |       | |        | |        | |      ||  ||        ||   |||       || ||       | | | |
                                               1 1      11  11        11   112       22 22       2 2 2 2
    1 23       4 5       6 7        8 9        0 1      23  45        67   890       12 34       5 6 7 8
    The numbers corresponding to the left arnd right edges of each umbrella are what I called ns_low and ns_high in the table.  Each employyes ns_low and ns_high numbers will be inside the range of each of his ancestors ns_low and ns_high.
    To find the ancestors of a given node in the nested set model you can do this:
    SELECT   a.empno, a.ename, a.ns_low, a.ns_high
    FROM   emp  a
    JOIN      emp  d  ON  d.ns_low  BETWEEN  a.ns_low
                                    AND      a.ns_high
    WHERE     d.ename  IN ('ADAMS')
    ORDER BY  d.ename
    ,         a.ns_low
    Both the Lineage and Nested Sets models are good for tree structures only, whereas the Adjacency Model can handle other kinds of graphs, including graphs with loops.
    Both the Lineage and Nested Sets models can be very difficult to maintain if the hierarchy is re-organized.
    I'd like to repeat some of the warnings that others have made.  You could write separate code for each system (Oracle, SQL Server, ...) that you want to run in, and the code for each system will be more or less different.  You're looking for some code that will get the same results in all systems.  That code will be more complicated that the most complicated of the single-system versions, and it will be sloweer than the slwoest of the single-system versions.  You're giving up a lot of functionality, and probably also ease of maintenance, by writing code that has to work on multiple systems without changes.
    Here's how I created the emp table shown above from scott.emp:
    CREATE TABLE    emp
    AS
    WITH    connect_by_results  AS
        SELECT  empno, mgr, ename
        ,       LEVEL   AS lvl
        ,       ROWNUM  AS r_num
        ,       SYS_CONNECT_BY_PATH (empno, '/') || '/'   AS lineage
        FROM scott.emp
        START WITH mgr IS NULL
        CONNECT BY mgr = PRIOR empno
        ORDER SIBLINGS BY ename
    SELECT empno, mgr, ename, lineage
    ,       (2 * r_num) - lvl            AS ns_low
    ,       (2 * r_num) + ( 2 * (
                                    SELECT  COUNT (*)
                                    FROM    connect_by_results
                                    WHERE   lineage  LIKE '%/' || cbr.empno || '/%'
                        - (lvl + 1)      AS ns_high
    FROM connect_by_results   cbr
    This relies on the fact that the hierarchy in scott.emp has only one root (that is, a node with no parent).  Computing the Nested Sets numbers is a little more complicated if you can have multiple roots.

  • Hierarchical Query Duplication Issue

    Hi All,
    I am trying to create a tree structure for a "NOT SO" elegantly structured data(for tree type traversing). The below mentioned query is the sample of the data that I have in the real-time system.
    The assumption & requirements to be considered are as follows:
    1. The query with the rep data(i.e. first WITH clause query with 9 rows) is the master data that needs to be shown in hierarchical structure.
    2. The query with the group data is joined with the rep data on GROUP_ID to get the tree structure be traversing on group_id and PARENT_GROUP_ID(i.e. Second WITH clause query)
    The problem is I am getting 16 rows instead of 9(which are expected since the master rep data query has 9 rows). Some part of the Tress is getting wrongly duplicated.
    Can anybody please point to me where I am making any mistake in the way this hierarchical query is written.
    SELECT
      REP_EMAIL
    ,REP_TYPE
    ,GROUP_MGR_EMAIL
    ,GROUP_ID
    ,PARENT_GROUP_ID
    ,RPAD('*',LEVEL*10,'*')||REP_EMAIL REP_EMAIL
    -- ,RPAD('*',LEVEL*10,'*')||GROUP_MGR_EMAIL GROUP_MGR_EMAIL
    ,SYS_CONNECT_BY_PATH(REP_EMAIL, '/') "Path"
      FROM (
          SELECT * FROM (
                       WITH REP AS(
                                     SELECT '[email protected]' AS EMAIL, 'REP' AS REP_TYPE, 112 AS GROUP_ID, 112 AS PARENT_GROUP_ID FROM DUAL UNION
                                     SELECT '[email protected]', 'REP' , 112 , 112 FROM DUAL UNION
                                     SELECT '[email protected]', 'REP' , 115 , 115 FROM DUAL UNION
                                     SELECT '[email protected]', 'REP' , 115 , 115 FROM DUAL UNION
                                     SELECT '[email protected]', 'MGR' , 112 , 117  FROM DUAL UNION
                                     SELECT '[email protected]', 'MGR' , 115 , 119 FROM DUAL UNION
                                     SELECT '[email protected]', 'MGR' , 117 , 2 FROM DUAL UNION
                                     SELECT '[email protected]', 'MGR' , 119 , 2 FROM DUAL UNION
                                     SELECT '[email protected]', 'REP' , 115 , 115  FROM DUAL
                                     SELECT EMAIL AS REP_EMAIL, REP_TYPE, GROUP_ID AS REP_GROUP_ID, PARENT_GROUP_ID AS REP_PARENT_GROUP_ID
                                       FROM REP) REP
       JOIN (
             SELECT * FROM (
                         WITH GRP AS (
                                     SELECT 1 AS GROUP_ID, NULL AS PARENT_GROUP_ID, '[email protected]' AS GROUP_MGR_EMAIL FROM DUAL UNION
                                     SELECT 2 , 1 , '[email protected]' FROM DUAL UNION
                                     SELECT 50 , 2 , '[email protected]' FROM DUAL UNION
                                     SELECT 112 , 117 , '[email protected]' FROM DUAL UNION
                                     SELECT 115 , 119 , '[email protected]' FROM DUAL UNION
                                     SELECT 117 , 2 , '[email protected]' FROM DUAL UNION
                                     SELECT 119 , 2 , '[email protected]' FROM DUAL
                                    SELECT GROUP_ID,  PARENT_GROUP_ID, GROUP_MGR_EMAIL
                                      FROM GRP) GRP) GRP
       ON (REP.REP_PARENT_GROUP_ID = GRP.GROUP_ID))
        START WITH PARENT_GROUP_ID = 1
      CONNECT BY   PARENT_GROUP_ID = PRIOR GROUP_IDAny help would be really appreciated.
    Thank you,
    Warm Regards
    Goldi

    Goldi wrote:
    The problem is I am getting 16 rows instead of 9(which are expected since the master rep data query has 9 rows). Some part of the Tress is getting wrongly duplicated. I don't think it is getting wrongly duplicated...
    SQL> ed
    Wrote file afiedt.buf
      1  WITH REP AS (SELECT EMAIL AS REP_EMAIL, REP_TYPE, GROUP_ID AS REP_GROUP_ID, PARENT_GROUP_ID AS REP_PARENT_GROUP_ID
      2               FROM (
      3                     SELECT '[email protected]' AS EMAIL, 'REP' AS REP_TYPE, 112 AS GROUP_ID, 112 AS PARENT_GROUP_ID FROM DUAL UNION
      4                     SELECT '[email protected]'        , 'REP'            , 112            , 112            FROM DUAL UNION
      5                     SELECT '[email protected]'        , 'REP'            , 115            , 115            FROM DUAL UNION
      6                     SELECT '[email protected]'        , 'REP'            , 115            , 115            FROM DUAL UNION
      7                     SELECT '[email protected]'         , 'MGR'            , 112            , 117            FROM DUAL UNION
      8                     SELECT '[email protected]'        , 'MGR'            , 115            , 119            FROM DUAL UNION
      9                     SELECT '[email protected]'        , 'MGR'            , 117            , 2              FROM DUAL UNION
    10                     SELECT '[email protected]'        , 'MGR'            , 119            , 2              FROM DUAL UNION
    11                     SELECT '[email protected]'         , 'REP'            , 115            , 115            FROM DUAL
    12                    )
    13              )
    14      ,GRP AS (
    15               SELECT 1 AS GROUP_ID, NULL AS PARENT_GROUP_ID, '[email protected]' AS GROUP_MGR_EMAIL FROM DUAL UNION
    16               SELECT 2            , 1                      , '[email protected]'                   FROM DUAL UNION
    17               SELECT 50           , 2                      , '[email protected]'                   FROM DUAL UNION
    18               SELECT 112          , 117                    , '[email protected]'                     FROM DUAL UNION
    19               SELECT 115          , 119                    , '[email protected]'                    FROM DUAL UNION
    20               SELECT 117          , 2                      , '[email protected]'                    FROM DUAL UNION
    21               SELECT 119          , 2                      , '[email protected]'                    FROM DUAL
    22              )
    23  --
    24  SELECT REP_EMAIL
    25        ,REP_TYPE
    26        ,GROUP_MGR_EMAIL
    27        ,GROUP_ID
    28        ,PARENT_GROUP_ID
    29       -- ,RPAD('*',LEVEL*10,'*')||REP_EMAIL REP_EMAIL
    30       -- ,RPAD('*',LEVEL*10,'*')||GROUP_MGR_EMAIL GROUP_MGR_EMAIL
    31       -- ,SYS_CONNECT_BY_PATH(REP_EMAIL, '/') "Path"
    32  FROM   REP JOIN GRP ON (REP.REP_PARENT_GROUP_ID = GRP.GROUP_ID)
    33  --CONNECT BY PARENT_GROUP_ID = PRIOR GROUP_ID
    34* --START WITH PARENT_GROUP_ID = 1
    SQL> /
    REP_EMAIL                                                    REP GROUP_MGR_EMAIL     GROUP_ID PARENT_GROUP_ID
    [email protected]                                             REP [email protected]          112          117
    [email protected]                                             REP [email protected]         115          119
    [email protected]                                             REP [email protected]         115          119
    [email protected]                                              REP [email protected]          112          117
    [email protected]                                              REP [email protected]         115          119
    [email protected]                                             MGR [email protected]         119            2
    [email protected]                                             MGR [email protected]          2            1
    [email protected]                                             MGR [email protected]          2            1
    [email protected]                                              MGR [email protected]         117            2
    9 rows selected.There are multiple matches in REP for the parent group id's. e.g. xyz2@.. and xyz3@.. both have a parent of 2 so you'll get duplicated branches from that as the connect by is going on the group id's e.g.
    1
      2
         117
             112
             112
         119
             115
             115
             115
      2
         117
             112
             112
         119
             115
             115
             115The duplicates are caused by the rows in REP. So Oracle is doing what you are asking of it because there is nothing further to restrict the connection to make one branch of 2 unique from the other branch of 2

  • Joins in Hierarchical Query

    I'm trying to use a Hierarchical Query with table joins in the "FROM" clause. According to the documentation, this will work with Oracle 9i and above.
    My problem is that in some queries, using the "JOIN" syntax works correctly but in others, it gives an "ORA-00928" message.
    If I change the query that gives the error to use old style Oracle joins (using the where clause), the query works.
    Can anyone help me to identify the problem?

    Here's the query. Again, note that if I use the old oracle join syntax (using where clause) it works:
    select level
    , biu.parent_part_no
    , biu.parent_part_serial_no
    , biu.comp_part_qty
    , cpi.part_no
    , cpi.serial_number
    , cpi.trace_code
    from bom.bom_instance_usage biu
    join bom.bom_part_instance cpi
    on cpi.bom_part_instance_key = iu.comp_part_instance_key
    start with ( biu.parent_part_no = '123456'
    and biu.parent_part_serial_no = '114' )
    connect by ( biu.parent_part_no = prior cpi.part_no
    and biu.parent_part_serial_no = prior cpi.serial_number )

Maybe you are looking for