I’m encountering some issues with hierarchical structure query in Oracle. Specifically, I’m trying to build a query to represent a hierarchical structure, but the results are not as expected. Here are the details: I created a table in my Oracle database as:
CREATE TABLE EMP
(
DNO NUMBER,
ENAME VARCHAR2(10),
SAL NUMBER
);
INSERT INTO EMP (DNO, ENAME, SAL) VALUES
(101, ‘John’, 3000),
(101, ‘Michael’, 2500),
(101, ‘Scott’, 4000),
(102, ‘Smith’, 1500),
(102, ‘Williams’, 2000),
(102, ‘James’, 3500),
(103, ‘Andrew’, 1000),
(103, ‘Becker’, 4500);
When I inserted the above data, my EMP table looked like this:
DNO | ENAME | SAL |
---|---|---|
101 | John | 3000 |
101 | Michael | 2500 |
101 | Scott | 4000 |
102 | Smith | 1500 |
102 | Williams | 2000 |
102 | James | 3500 |
103 | Andrew | 1000 |
103 | Becker | 4500 |
I want my output to be displayed in a hierarchical structure where the DNO is shown only once, followed by its ENAME and SAL. My expected output is as follows:
DNO | ENAME | SAL |
---|---|---|
101 | John | 3000 |
Michael | 2500 | |
Scott | 4000 | |
102 | Smith | 1500 |
Williams | 2000 | |
James | 3500 | |
103 | Andrew | 1000 |
Becker | 4500 |
I tried several ways, but they didn’t work. Can someone help me? Thanks in advance.
Your data is not really hierarchical, because there are no column(s) which provide for the perceived ordering you think is there. Your actual requirement is to sort by DNO
group, and to then display the DNO
value only once, for the “first” entry. We can approximate what you want using ROW_NUMBER()
:
WITH cte AS (
SELECT e.*, ROW_NUMBER() OVER (PARTITION BY DNO ORDER BY DBMS_RANDOM.VALUE) rn
FROM EMP e
)
SELECT CASE WHEN rn = 1 THEN DNO END AS DNO, ENAME, SAL
FROM cte t
ORDER BY t.DNO, rn;
Demo
If you have some logic by which we know which employee to report first for a given block of DNO
, then you should add these details (and columns) to the question.
4
If you are using SQL*Plus (or another client application that supports SQL*Plus commands) then you can use BREAK
:
BREAK ON dno
SELECT * FROM emp ORDER BY dno, ename;
If you are not using a client application that supports BREAK
then you can use the ROW_NUMBER
analytic function to number the rows and then a CASE
expression and TO_CHAR
to format the number as a string or a space character for later rows in each group:
SELECT CASE rn
WHEN 1
THEN TO_CHAR(dno)
ELSE ' '
END AS dno,
ename,
sal
FROM (
SELECT dno,
ename,
sal,
ROW_NUMBER() OVER (PARTITION BY dno ORDER BY ename) AS rn
FROM emp
) e
ORDER BY
e.dno,
e.ename
Which outputs:
DNO | ENAME | SAL |
---|---|---|
101 | John | 3000 |
Michael | 2500 | |
Scott | 4000 | |
102 | James | 3500 |
Smith | 1500 | |
Williams | 2000 | |
103 | Andrew | 1000 |
Becker | 4500 |
fiddle