Oracle Hierarchical Query output format - oracle

I have a table that list all the security project access numbers that you can possibly have for a given user. I am using Oracle db,
Security
---------
8055
8055.01
8056
8056.00
8056.01
8056.01.01
9005.01.02
9876.03
I am trying to get an output table in a format useful for our treeview application
Security Parent ID Group ID Level No
------------------------------------------------
8055 null 1 1
8055.01 8055 1 2
8056 null 2 1
8056.00 8056 2 2
8056.01 8056 2 2
8056.01.01 8056.01 2 3
9005.01.02 null 3 1
9876.03 null 4 1
can any one help me to get the above output format ?

Oracle Setup:
CREATE TABLE Table_name ( Security ) AS
SELECT '8055' FROM DUAL UNION ALL
SELECT '8055.01' FROM DUAL UNION ALL
SELECT '8056' FROM DUAL UNION ALL
SELECT '8056.00' FROM DUAL UNION ALL
SELECT '8056.01' FROM DUAL UNION ALL
SELECT '8056.01.01' FROM DUAL UNION ALL
SELECT '9005.01.02' FROM DUAL UNION ALL
SELECT '9876.03' FROM DUAL;
Query:
WITH parents ( security, parent_id ) AS (
SELECT security,
SUBSTR( security, 1, INSTR( security, '.', -1 ) - 1 )
FROM table_name
)
SELECT security,
PRIOR security AS parent_id,
DENSE_RANK() OVER ( ORDER BY CONNECT_BY_ROOT( security ) ) AS group_id,
LEVEL AS level_no
FROM parents p
START WITH NOT EXISTS ( SELECT 'X'
FROM table_name t
WHERE t.security = p.parent_id )
CONNECT BY PRIOR security = parent_id
ORDER BY security;
Output:
SECURITY PARENT_ID GROUP_ID LEVEL_NO
---------- ---------- ---------- ----------
8055 1 1
8055.01 8055 1 2
8056 2 1
8056.00 8056 2 2
8056.01 8056 2 2
8056.01.01 8056.01 2 3
9005.01.02 3 1
9876.03 4 1

Related

Sum 2 columns from multiple rows with same ID

Currently, I have a table MY_TABLE like below:
ID ACCT_TYPE CREDIT_AMT DEBIT_AMT
-- --------- ---------- ---------
1 CDT_01 4 (null)
1 DBT_01 (null) 6
One ID can have multiple ACCT_TYPE like above, and each type has its own amount.
I want to just select the row which has ACCT_TYPE like 'CDT_%' but also the total_amount column which is the total of credit_amt and debit_amt column for the same ID.
My expected output like below:
ID ACCT_TYPE TOTAL_AMT
-- --------- ---------
1 CDT_01 10
I tried with this select statement below but it's no use, I think it's because of different ACCT_TYPE:
Select ID, ACCT_TYPE, SUM(NVL(CREDIT_AMT, 0) + NVL(DEBIT_AMT, 0)) TOTAL_AMT
FROM MY_TABLE WHERE ACCT_TYPE LIKE 'CDT_%' GROUP BY ID, ACCT_TYPE;
Here is the output of the select statement above:
ID ACCT_TYPE TOTAL_AMT
-- --------- ---------
1 CDT_01 4
I just begin to learn some query so I don't know is it really possible to get my expected output.
One way to do it is like below:
with inputs (ID, ACCT_TYPE, CREDIT_AMT, DEBIT_AMT) as
(
select 1, 'CDT_01', 4, null from dual union all
select 1, 'DBT_01', null, 6 from dual
),
prep as
(
select t.*, sum(nvl(credit_amt,0)) over (partition by id) + sum(nvl(debit_amt,0)) over (partition by id) as sum_per_id
from inputs t
)
select id, acct_type, sum_per_id
from prep
where acct_type like 'CDT_%';
Output:
A correlated subquery might be one option; sample data (thank you, #Ranagal) in lines #1 - 5; query that does the job begins at line #6.
SQL> with inputs (ID, ACCT_TYPE, CREDIT_AMT, DEBIT_AMT) as
2 (
3 select 1, 'CDT_01', 4, null from dual union all
4 select 1, 'DBT_01', null, 6 from dual
5 )
6 select a.id,
7 a.acct_type,
8 (select sum(nvl(b.credit_amt, 0)) +
9 sum(nvl(b.debit_amt , 0))
10 from inputs b
11 where b.id = a.id
12 ) total_amt
13 from inputs a
14 where acct_type like 'CDT%';
ID ACCT_T TOTAL_AMT
---------- ------ ----------
1 CDT_01 10
SQL>

Oracle - creating a group based on group spacing

How to make a query that will create groups that have a space between them greater than "n"?
Data:
01-01-2000
02-01-2000
03-01-2000
06-01-2000
07-01-2000
19-02-2001
10-01-2002
11-01-2002
I would like to get a result for the interval between records, e.g. 2 days:
DATE GROUP
01-01-2000 1
02-01-2000 1
03-01-2000 1
06-01-2000 2
07-01-2000 2
19-02-2001 3
10-01-2002 4
11-01-2002 4
For 10 days:
01-01-2000 1
02-01-2000 1
03-01-2000 1
06-01-2000 1
07-01-2000 1
19-02-2001 2
10-01-2002 3
11-01-2002 3
Another example with integers:
with x as (
select 1 as A from dual
union all
select 2 as A from dual
union all
select 3 as A from dual
union all
select 10 as A from dual
union all
select 20 as A from dual
union all
select 22 as A from dual
union all
select 33 as A from dual
union all
select 40 as A from dual
union all
select 50 as A from dual
union all
select 100 as A from dual
union all
select 101 as A from dual
union all
select 102 as A from dual
) select A
from x;
I need to create groups for a value increase of more than 3:
Example result:
1 1
2 1
3 1
10 2
20 3
22 3
33 4
40 5
50 6
100 7
101 7
102 7
Here is one way to do it
CREATE TABLE TEST (
DATE_IN DATE
);
INSERT INTO TEST VALUES (TO_DATE('01-01-2000','DD-MM-YYYY'));
INSERT INTO TEST VALUES (TO_DATE('02-01-2000','DD-MM-YYYY'));
INSERT INTO TEST VALUES (TO_DATE('03-01-2000','DD-MM-YYYY'));
INSERT INTO TEST VALUES (TO_DATE('06-01-2000','DD-MM-YYYY'));
INSERT INTO TEST VALUES (TO_DATE('07-01-2000','DD-MM-YYYY'));
INSERT INTO TEST VALUES (TO_DATE('19-02-2001','DD-MM-YYYY'));
INSERT INTO TEST VALUES (TO_DATE('10-01-2002','DD-MM-YYYY'));
INSERT INTO TEST VALUES (TO_DATE('11-01-2002','DD-MM-YYYY'));
--HERE IS AN EXAMPLE FOR 1 DAY. Just change the value in the > 1 TO >10
--if you want to create a group if there is a gap of more than 10days
SELECT DATE_IN, SUM(NEW_GROUP) OVER ( ORDER BY DATE_IN) AS GROUPE FROM (
SELECT
DATE_IN,
CASE WHEN DATE_IN - LAG(DATE_IN,1,TO_DATE('01-01-1900','DD-MM-YYYY')) OVER ( ORDER BY DATE_IN) > 1 THEN 1 ELSE 0 END AS NEW_GROUP
FROM TEST
)
-- Result
DATE_IN GROUPE
2000-01-01T00:00:00Z 1
2000-01-02T00:00:00Z 1
2000-01-03T00:00:00Z 1
2000-01-06T00:00:00Z 2
2000-01-07T00:00:00Z 2
2001-02-19T00:00:00Z 3
2002-01-10T00:00:00Z 4
2002-01-11T00:00:00Z 4
Example with integer:
with x as (
select 1 as A from dual
union all
select 2 as A from dual
union all
select 3 as A from dual
union all
select 10 as A from dual
union all
select 20 as A from dual
union all
select 22 as A from dual
union all
select 33 as A from dual
union all
select 40 as A from dual
union all
select 50 as A from dual
union all
select 100 as A from dual
union all
select 101 as A from dual
union all
select 102 as A from dual
) SELECT A, SUM(NEW_GROUP) OVER ( ORDER BY A) AS GROUPE FROM (
SELECT
A,
CASE WHEN A - LAG(A,1,1) OVER ( ORDER BY A) > 5 THEN 1 ELSE 0 END AS NEW_GROUP
FROM X
)
order by A;

Find Maximal Value of other Rows per Group

I have a simple table with values (ID) in groups (GRP_ID).
create table tst as
select 1 grp_id, 1 id from dual union all
select 1 grp_id, 1 id from dual union all
select 1 grp_id, 2 id from dual union all
select 2 grp_id, 1 id from dual union all
select 2 grp_id, 2 id from dual union all
select 2 grp_id, 2 id from dual union all
select 3 grp_id, 3 id from dual;
It is straightforward to find a maximum value per group using analytical functions.
select grp_id, id,
max(id) over (partition by grp_id) max_grp
from tst
order by 1,2;
GRP_ID ID MAX_GRP
---------- ---------- ----------
1 1 2
1 1 2
1 2 2
2 1 2
2 2 2
2 2 2
3 3 3
But the goal is to find the maximum value excluding the value of the current row.
This is the expected result (column MAX_OTHER_ID):
GRP_ID ID MAX_GRP MAX_OTHER_ID
---------- ---------- ---------- ------------
1 1 2 2
1 1 2 2
1 2 2 1
2 1 2 2
2 2 2 2
2 2 2 2
3 3 3
Note that in the GRP_ID = 2 a tie on the MAX value exists, so the MAX_OTHER_ID remains the same.
I did manage this two step solution, but I'm wondering if there is a more straightforward and simple solution.
with max1 as (
select grp_id, id,
row_number() over (partition by grp_id order by id desc) rn
from tst
)
select GRP_ID, ID,
case when rn = 1 /* MAX row per group */ then
max(decode(rn,1,to_number(null),id)) over (partition by grp_id)
else
max(id) over (partition by grp_id)
end as max_other_id
from max1
order by 1,2
;
I wish the window functions supported multiple range specifications something like:
max(id) over (
partition by grp_id
order by id
range between unbounded preceding and 1 preceding
or range between 1 following and unbounded following
)
But unfortunately they don't.
As a workaround, you can avoid subqueries and CTEs using the function twice on the different ranges and call coalesce on that.
select grp_id,
id,
coalesce(
max(id) over (
partition by grp_id
order by id
range between 1 following and unbounded following
)
, max(id) over (
partition by grp_id
order by id
range between unbounded preceding and 1 preceding
)
) max_grp
from tst
order by 1,
2
Coalesce works out of the box because of the ordering clause as the result of the window function call will be either the max in the given window or a null value.
Demo - http://rextester.com/SDXVF13962
SELECT GRP_ID,ID, (SELECT Max(ID) FROM TEST A WHERE A.ROWID<>B.ROWID AND A.GRP_ID=B.GRP_ID) maX_ID FROM TEST B;
Got the expected result with Co-Related Query ! Hope this helps .

How to combine 2 select statements in oracle

i have table test2.it contains
ID
1
4
5
10
now i found missing numbers in this sequence.with this query
SELECT min_ID - 1 + level mn FROM
( SELECT MIN(ID) min_ID , MAX(ID) max_ID FROM test2 )
CONNECT BY level <= max_ID - min_ID + 1 minus SELECT ID FROM test2
output is:
MN
---
2
3
6
7
8
9
now i want to combine these 2 columns.I am unable to do this please help me.
i want output like
1 2
4 3
7 5
10 6
8
9
Oracle Setup:
CREATE TABLE test2 (id) AS
SELECT 1 FROM DUAL UNION ALL
SELECT 4 FROM DUAL UNION ALL
SELECT 5 FROM DUAL UNION ALL
SELECT 10 FROM DUAL;
Query:
WITH bounds ( mn, mx ) AS (
SELECT MIN( id ), MAX( id ) FROM test2
),
missing (id, rn) AS (
SELECT id, ROWNUM
FROM (
SELECT mn + LEVEL AS id
FROM bounds
CONNECT BY LEVEL < MX - MN
MINUS
SELECT id
FROM test2
)
),
existing ( id, rn ) AS (
SELECT id, ROWNUM
FROM test2
)
SELECT e.id, m.id
FROM existing e
FULL OUTER JOIN
missing m
ON ( e.rn = m.rn );
Output
ID ID
---------- ----------
1 2
4 3
5 6
10 7
9
8

scalar subquery has an aggregate operation

My oracle version is 10.2.
It's very strange when a scalar subquery has an aggregate operation.
my table named t_test looked like this;
t_id t_name
1 1
2 1
3 2
4 2
5 3
6 3
query string looked like this;
select t1.t_id,
(select count(t_name)
from (select t2.t_name
from t_test t2
where t2.t_id=t1.t_id
group by t2.t_name)) a
from t_test t1
this query's result is,
t_id a
1 3
2 3
3 3
4 3
5 3
6 3
which is very weird,
take t1.t_id=1 for example,
select count(t_name)
from (select t2.t_name
from t_test t2
where t2.t_id=1
group by t2.t_name)
the result is 1,
somehow,the 'where' operator doesn't work,the result is exactly the same as I put my query like this:
select t1.t_id,
(select count(t_name)
from (select t2.t_name
from t_test t2
group by t2.t_name)) a
from t_test t1
why?
Can you post a cut-and-paste from SQL*Plus showing exactly what query you're running? The query you posted does not appear to be valid-- the alias t1 is not going to be valid in the subquery where you're referencing it. That makes me suspect that you're simplifying the problem to post here but you've accidentally left something important out.
SQL> ed
Wrote file afiedt.buf
1 with x as (
2 select 1 id, 1 name from dual union all
3 select 2,1 from dual union all
4 select 3,2 from dual union all
5 select 4,2 from dual union all
6 select 5,3 from dual union all
7 select 6,3 from dual
8 )
9 select t1.id
10 ,(select count(b.name)
11 from (select t2.name
12 from x t2
13 where t2.id = t1.id
14 group by t2.name) b) a
15* from x t1
SQL> /
where t2.id = t1.id
*
ERROR at line 13:
ORA-00904: "T1"."ID": invalid identifier
Presumably, it would be much more natural to write the query like this (assuming you really want to use a scalar subquery) where t1 is going to be a valid alias in the scalar subquery.
SQL> ed
Wrote file afiedt.buf
1 with x as (
2 select 1 id, 1 name from dual union all
3 select 2,1 from dual union all
4 select 3,2 from dual union all
5 select 4,2 from dual union all
6 select 5,3 from dual union all
7 select 6,3 from dual
8 )
9 select t1.id
10 ,(select count(t2.name)
11 from x t2
12 where t2.id = t1.id) cnt
13* from x t1
SQL> /
ID CNT
---------- ----------
1 1
2 1
3 1
4 1
5 1
6 1
6 rows selected.

Resources