36
Accessing Hierarchical Data in Oracle Carol-Lee Erikson Tutch [email protected]

Accessing Hierarchical Data in Oracle

  • Upload
    bryson

  • View
    47

  • Download
    1

Embed Size (px)

DESCRIPTION

Accessing Hierarchical Data in Oracle. Carol-Lee Erikson Tutch [email protected]. Hierarchically Structured Data. Items at different levels are similar Arbitrary number of levels Examples Personnel Officer Manager Contributor Manufacturing Product Assembly Part. - PowerPoint PPT Presentation

Citation preview

Page 1: Accessing Hierarchical Data in Oracle

Accessing Hierarchical Data in Oracle

Carol-Lee Erikson [email protected]

Page 2: Accessing Hierarchical Data in Oracle

Hierarchically Structured Data• Items at different levels are similar• Arbitrary number of levels• Examples

– Personnel• Officer• Manager• Contributor

– Manufacturing• Product• Assembly• Part

Page 3: Accessing Hierarchical Data in Oracle

Representing a Hierarchy• Items have a Parent-Child relationship• Hierarchy can be visualized as a Tree• Item is called a Node• Connections btw nodes are Links• Node has 0 or 1 parent• Node w/o parent is a Root• Node has 0 to N children• Node w/o children is a Leaf

Page 4: Accessing Hierarchical Data in Oracle

Level 1

Level 2

Level 3

Level 4

Level 5

Hierarchy Tree1

4

2 9 15

3

65

11107

8

14

1312

Page 5: Accessing Hierarchical Data in Oracle

ER Diagram

Page 6: Accessing Hierarchical Data in Oracle

Structure for Sample DataCREATE TABLE emp ( emp_id NUMBER(5) NOT NULL, emp_name VARCHAR2(30) NOT NULL, mgr_emp_id NUMBER(5), dept_cd VARCHAR2(6), sal NUMBER(9,2), hire_dt DATE NOT NULL ); CREATE UNIQUE INDEX emp_pk ON emp ( emp_id ); ALTER TABLE emp ADD CONSTRAINT emp_pk PRIMARY KEY ( emp_id ) USING INDEX emp_pk; CREATE INDEX emp_f1 ON emp ( mgr_emp_id ASC ); ALTER TABLE emp ADD CONSTRAINT emp_f1 FOREIGN KEY ( mgr_emp_id ) REFERENCES emp ( emp_id );

Page 7: Accessing Hierarchical Data in Oracle

Sample DataSELECT * FROM emp ORDER BY emp_id;

EMP_ID EMP_NAME MGR_EMP_ID DEPT_CD SAL HIRE_DT10Barbara PRES 300,000 01-Jan-2001 11John 10PRES 50,000 03-Jan-2001 12Edward 10SALES 100,000 01-Feb-2003 13Josh 12SALES 70,000 01-Mar-2004 14Beth 10IT 150,000 01-May-2001 15Rich 14IT 90,000 01-Jul-2001 16George FIN 200,000 01-Feb-2002 17Barby 12SALES 100,000 01-Apr-2005 21Michelle 14IT 70,000 02-Jan-2002 22Tom 14IT 80,000 01-Feb-2004 24John 16FIN 100,000 03-Jan-2003 28Sam 14IT 110,000 01-Jan-2001 31Robin 28IT 60,000 01-Jun-2005 32Jane 24FIN 60,000 01-Feb-2004 33Mary 16FIN 70,000 01-Mar-2005

Page 8: Accessing Hierarchical Data in Oracle

Sample Data Graph10

Barbara16

George

11John

12Edward

14Beth

13Josh

15Rich

17Barby

21Michelle

22Tom

24John

33Mary

28Sam

31Robin

32Jane

Page 9: Accessing Hierarchical Data in Oracle

Employees & SupervisorsSELECT e.emp_id, e.emp_name, m.emp_id AS mgr_id, m.emp_name AS mgr_name FROM emp e JOIN emp m ON e.mgr_emp_id = m.emp_id ORDER BY e.emp_id;

EMP_ID EMP_NAME MGR_ID MGR_NAME11John 10Barbara12Edward 10Barbara13Josh 12Edward14Beth 10Barbara15Rich 14Beth17Barby 12Edward21Michelle 14Beth22Tom 14Beth24John 16George28Sam 14Beth31Robin 28Sam32Jane 24John33Mary 16George

Page 10: Accessing Hierarchical Data in Oracle

… Add Top-Level SupervisorsSELECT e.emp_id, e.emp_name, m.emp_id AS mgr_id, m.emp_name AS mgr_name FROM emp e LEFT JOIN emp m ON e.mgr_emp_id = m.emp_id ORDER BY e.emp_id;

EMP_ID EMP_NAME MGR_ID MGR_NAME10Barbara11John 10Barbara12Edward 10Barbara13Josh 12Edward14Beth 10Barbara15Rich 14Beth16George17Barby 12Edward21Michelle 14Beth22Tom 14Beth24John 16George28Sam 14Beth31Robin 28Sam32Jane 24John33Mary 16George

Page 11: Accessing Hierarchical Data in Oracle

… Add Supervisor’s SupervisorsSELECT e.emp_id, e.emp_name, m1.emp_id AS mgr_id1, m1.emp_name AS gr_name1, m2.emp_id AS mgr_id2, m2.emp_name AS mgr_name2 FROM emp e LEFT JOIN emp m1 ON e.mgr_emp_id = m1.emp_id LEFT JOIN emp m2 ON m1.mgr_emp_id = m2.emp_id ORDER BY e.emp_id;

EMP_ID EMP_NAME MGR_ID1 MGR_NAME1 MGR_ID2 MGR_NAME210Barbara11John 10Barbara12Edward 10Barbara13Josh 12Edward 10Barbara14Beth 10Barbara15Rich 14Beth 10Barbara16George17Barby 12Edward 10Barbara21Michelle 14Beth 10Barbara22Tom 14Beth 10Barbara24John 16George28Sam 14Beth 10Barbara31Robin 28Sam 14Beth32Jane 24John 16George33Mary 16George

Page 12: Accessing Hierarchical Data in Oracle

All Root NodesSELECT e.emp_id, e.emp_name FROM emp e WHERE e.mgr_emp_id IS NULL;

EMP_ID EMP_NAME10Barbara16George

Page 13: Accessing Hierarchical Data in Oracle

All Leaf NodesSELECT e.emp_id, e.emp_name FROM emp e WHERE e.emp_id NOT IN ( SELECT DISTINCT m.mgr_emp_id FROM emp m WHERE m.mgr_emp_id IS NOT NULL );

EMP_ID EMP_NAME11John13Josh15Rich21Michelle22Tom17Barby31Robin32Jane33Mary

Page 14: Accessing Hierarchical Data in Oracle

Oracle Extensions for Hierarchies• CONNECT BY clause

– Allows linking w/n same range variable (table alias)– Occurs after WHERE clause– Executes before WHERE clause

• PRIOR keyword– Indicates how to connect w/ last record retrieved– Can occur on either side of the join– Can be used multiple times

• START WITH clause– Indicates record(s) to initially retrieve– Optional: w/o, initially starts w/ all records

Page 15: Accessing Hierarchical Data in Oracle

Basic Hierarchy QuerySELECT e.emp_id, e.emp_name, e.mgr_emp_id FROM emp e START WITH e.mgr_emp_id IS NULLCONNECT BY PRIOR e.emp_id = e.mgr_emp_id;

EMP_ID EMP_NAME MGR_EMP_ID10Barbara11John 1012Edward 1013Josh 1217Barby 1214Beth 1015Rich 1421Michelle 1422Tom 1428Sam 1431Robin 2816George24John 1632Jane 2433Mary 16

Page 16: Accessing Hierarchical Data in Oracle

Adding the Manager NameSELECT e.emp_id AS "E ID", e.emp_name AS "Employee", m.emp_id AS "M ID", m.emp_name AS "Manager" FROM ( SELECT e.emp_id, e.mgr_emp_id FROM emp e START WITH e.mgr_emp_id IS NULL CONNECT BY PRIOR e.emp_id = e.mgr_emp_id ) h JOIN emp e ON h.emp_id = e.emp_id JOIN emp m ON h.mgr_emp_id = m.emp_id ORDER BY e.emp_id; E ID Employee M ID Manager

11John 10Barbara12Edward 10Barbara13Josh 12Edward14Beth 10Barbara15Rich 14Beth17Barby 12Edward21Michelle 14Beth22Tom 14Beth24John 16George28Sam 14Beth31Robin 28Sam32Jane 24John33Mary 16George

Page 17: Accessing Hierarchical Data in Oracle

Start w/ any Record SetSELECT e.emp_id, e.emp_name, e.mgr_emp_id FROM emp e START WITH e.emp_name = 'Beth‘CONNECT BY e.mgr_emp_id = PRIOR e.emp_id;

EMP_ID EMP_NAME MGR_EMP_ID14Beth 1015Rich 1421Michelle 1422Tom 1428Sam 1431Robin 28

Page 18: Accessing Hierarchical Data in Oracle

Indicating LEVELSELECT LEVEL, e.emp_id, e.emp_name, e.mgr_emp_id FROM emp e START WITH e.emp_name = 'Beth‘CONNECT BY PRIOR e.emp_id = e.mgr_emp_id;

LEVEL EMP_ID EMP_NAME MGR_EMP_ID1 14Beth 102 15Rich 142 21Michelle 142 22Tom 142 28Sam 143 31Robin 28

Page 19: Accessing Hierarchical Data in Oracle

Use Functions on LEVELSELECT MAX(level) AS maxlevel FROM emp e START WITH e.mgr_emp_id IS NULLCONNECT BY PRIOR e.emp_id = e.mgr_emp_id;

MAXLEVEL4

Page 20: Accessing Hierarchical Data in Oracle

Aggregate on LEVELSELECT level, COUNT ( emp_id ) FROM emp e START WITH e.mgr_emp_id IS NULLCONNECT BY PRIOR e.emp_id = e.mgr_emp_id GROUP BY level;

LEVEL COUNT ( EMP_ID )1 22 53 74 1

Page 21: Accessing Hierarchical Data in Oracle

Indenting DependentsSELECT LPAD ( ' ', 3 * ( level - 1 ) ) || e.emp_name AS "Employee", level, e.emp_id, e.mgr_emp_id FROM emp e START WITH e.mgr_emp_id IS NULLCONNECT BY PRIOR e.emp_id = e.mgr_emp_id;

Employee LEVEL EMP_ID MGR_EMP_IDBarbara 1 10 John 2 11 10 Edward 2 12 10 Josh 3 13 12 Barbara 3 17 12 Beth 2 14 10 Rich 3 15 14 Michelle 3 21 14 Tom 3 22 14 Sam 3 28 14 Robin 4 31 28George 1 16 John 2 24 16 Jane 3 32 24 Mary 2 33 16

Page 22: Accessing Hierarchical Data in Oracle

Filter a HierarchySELECT LPAD ( ' ', 3 * ( level - 1 ) ) || e.emp_name AS "Employee", level, e.emp_id, e.mgr_emp_id FROM emp e WHERE e.hire_dt < '1-Jan-2004' START WITH e.mgr_emp_id IS NULLCONNECT BY PRIOR e.emp_id = e.mgr_emp_id;

Employee LEVEL EMP_ID MGR_EMP_IDBarbara 1 10 John 2 11 10 Edward 2 12 10 Beth 2 14 10 Rich 3 15 14 Michelle 3 21 14 Sam 3 28 14George 1 16 John 2 24 16

Page 23: Accessing Hierarchical Data in Oracle

Top N Levels of HierarchySELECT LPAD ( ' ', 3 * ( level - 1 ) ) || e.emp_name AS "Employee", level, e.emp_id, e.mgr_emp_id FROM emp e WHERE level <= 2 START WITH e.mgr_emp_id IS NULLCONNECT BY PRIOR e.emp_id = e.mgr_emp_id;

Employee LEVEL EMP_ID MGR_EMP_IDBarbara 1 10 John 2 11 10 Edward 2 12 10 Beth 2 14 10George 1 16 John 2 24 16 Mary 2 33 16

Page 24: Accessing Hierarchical Data in Oracle

Sorting, the Wrong WaySELECT LPAD ( ' ', 3 * ( level - 1 ) ) || e.emp_name AS "Employee", level, e.emp_id, e.mgr_emp_id FROM emp e START WITH e.mgr_emp_id IS NULLCONNECT BY PRIOR e.emp_id = e.mgr_emp_id ORDER BY emp_name; Employee LEVEL EMP_ID MGR_EMP_ID

Barbara 1 10 Barby 3 17 12 Beth 2 14 10 Edward 2 12 10George 1 16 Jane 3 32 24 John 2 11 10 John 2 24 16 Josh 3 13 12 Mary 2 33 16 Michelle 3 21 14 Rich 3 15 14 Robin 4 31 28 Sam 3 28 14 Tom 3 22 14

Page 25: Accessing Hierarchical Data in Oracle

Sorting: The SIBLINGS ClauseSELECT LPAD ( ' ', 3 * ( level - 1 ) ) || e.emp_name AS "Employee", level, e.emp_id, e.mgr_emp_id FROM emp e START WITH e.mgr_emp_id IS NULLCONNECT BY PRIOR e.emp_id = e.mgr_emp_id ORDER SIBLINGS BY emp_name; Employee LEVEL EMP_ID MGR_EMP_ID

Barbara 1 10 Beth 2 14 10 Michelle 3 21 14 Rich 3 15 14 Sam 3 28 14 Robin 4 31 28 Tom 3 22 14 Edward 2 12 10 Barby 3 17 12 Josh 3 13 12 John 2 11 10George 1 16 John 2 24 16 Jane 3 32 24 Mary 2 33 16

Page 26: Accessing Hierarchical Data in Oracle

Display a PathSELECT SUBSTR ( SYS_CONNECT_BY_PATH ( e.emp_name, ' - ' ), 4 ) AS "Employee Path" FROM emp e START WITH e.mgr_emp_id IS NULLCONNECT BY PRIOR e.emp_id = e.mgr_emp_id ORDER SIBLINGS BY emp_name; Employee Path

BarbaraBarbara - BethBarbara - Beth - MichelleBarbara - Beth - RichBarbara - Beth - SamBarbara - Beth - Sam - RobinBarbara - Beth - TomBarbara - EdwardBarbara - Edward - BarbyBarbara - Edward - JoshBarbara - JohnGeorgeGeorge - JohnGeorge - John - JaneGeorge - Mary

Page 27: Accessing Hierarchical Data in Oracle

The Root for Each NodeSELECT e.emp_id, e.emp_name AS "Employee", CONNECT_BY_ROOT e.emp_name AS "Top Dog" FROM emp e START WITH e.mgr_emp_id IS NULLCONNECT BY PRIOR e.emp_id = e.mgr_emp_id;

EMP_ID Employee Top Dog10Barbara Barbara11John Barbara12Edward Barbara13Josh Barbara17Barbara Barbara14Beth Barbara15Rich Barbara21Michelle Barbara22Tom Barbara28Sam Barbara31Robin Barbara16George George24John George32Jane George33Mary George

Page 28: Accessing Hierarchical Data in Oracle

Has a Connection toSELECT * FROM emp e WHERE e.emp_name = 'Beth' START WITH e.emp_name = 'Robin‘CONNECT BY e.emp_id = PRIOR e.mgr_emp_id;

EMP_ID EMP_NAME MGR_EMP_ID DEPT_CD SAL HIRE_DT14Beth 10IT 150,000 01-May-2001

Page 29: Accessing Hierarchical Data in Oracle

Summng Dependent InfoSELECT t2.emp_id, t2.emp_name, t2.sal, ( SELECT SUM ( t1.sal ) FROM emp t1 START WITH t1.emp_id = t2.emp_id CONNECT BY t1.mgr_emp_id = PRIOR t1.emp_id ) AS sum_salary FROM emp t2;

EMP_ID EMP_NAME SAL SUM_SALARY10Barbara 300,000 1,180,00011John 50,000 50,00012Edward 100,000 270,00013Josh 70,000 70,00014Beth 150,000 560,00015Rich 90,000 90,00021Michelle 70,000 70,00022Tom 80,000 80,00017Barby 100,000 100,00028Sam 110,000 170,00031Robin 60,000 60,00016George 200,000 430,00024John 100,000 160,00032Jane 60,000 60,00033Mary 70,000 70,000

Page 30: Accessing Hierarchical Data in Oracle

Cycles in a HierarchyUPDATE emp e SET e.mgr_emp_id = 22 WHERE e.emp_id = 10; SELECT LPAD ( ' ', 3 * ( level - 1 ) ) || e.emp_name AS "Employee", level, e.emp_id, e.mgr_emp_id FROM emp e START WITH e.mgr_emp_id = 10CONNECT BY PRIOR e.emp_id = e.mgr_emp_id;

17:08:52 Error: ORA-01436: CONNECT BY loop in user data

Page 31: Accessing Hierarchical Data in Oracle

Running w/ ErrorsSELECT LPAD ( ' ', 3 * ( level - 1 ) ) || e.emp_name AS "Employee", level, e.emp_id, e.mgr_emp_id FROM emp e START WITH e.mgr_emp_id = 10CONNECT BY NOCYCLE PRIOR e.emp_id = e.mgr_emp_id;

Employee LEVEL EMP_ID MGR_EMP_IDJohn 1 11 10Edward 1 12 10 Josh 2 13 12 Barby 2 17 12Beth 1 14 10 Rich 2 15 14 Michelle 2 21 14 Tom 2 22 14 Barbara 3 10 22 John 4 11 10 Edward 4 12 10 Josh 5 13 12 Barby 5 17 12 Sam 2 28 14 Robin 3 31 28

Page 32: Accessing Hierarchical Data in Oracle

Trapping Cycle ErrorsSELECT e.emp_id, e.emp_name, CONNECT_BY_ISCYCLE FROM emp e START WITH e.mgr_emp_id = 10CONNECT BY NOCYCLE PRIOR e.emp_id = e.mgr_emp_id; ROLLBACK; EMP_ID EMP_NAME CONNECT_BY_ISCYCLE

11John 012Edward 013Josh 017Barby 014Beth 015Rich 021Michelle 022Tom 010Barbara 111John 012Edward 013Josh 017Barby 028Sam 031Robin 0

Page 33: Accessing Hierarchical Data in Oracle

Finding Leaf NodesSELECT e.emp_id, e.emp_name FROM emp e WHERE CONNECT_BY_ISLEAF = 1 START WITH e.mgr_emp_id IS NULLCONNECT BY PRIOR e.emp_id = e.mgr_emp_id;

EMP_ID EMP_NAME11John13Josh17Barby15Rich21Michelle22Tom31Robin32Jane33Mary

Page 34: Accessing Hierarchical Data in Oracle

Source of InformationMastering Oracle SQL,2nd Edition Sanjay Mishra &Alan Beaulieu O'Reilly Media, Inc., 2004ISBN-13: 978-0-596-00632-7496 pages

Page 35: Accessing Hierarchical Data in Oracle

Counter

SELECT level x FROM dualCONNECT BY level <= 100;

X

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

Page 36: Accessing Hierarchical Data in Oracle

Additional Tricks w/ CONNECT BY

• Finding potential flights using an INTERSECT of 2 CONNECT BY queries.

• @ http://gennick.com/flight.html

SQL Pocket Guide, 2nd EditionBy: Jonathan Gennick

Publisher: O'Reilly Media, Inc.Pub. Date: April 17, 2006

Print ISBN-13: 978-0-596-52688-7Pages in Print Edition: 192