Oracle hierarchy query to find parents and child values From a table - oracle

I have a source table which contains values as :
Col1 Col2
A B
B C
E F
F G
G H
X Y
In this scenario A is a parent and b is child of A
And C is a grand child of A, parent and it's child with grand child's should come in one single line.
So the expected output is
Output :
A B C
E F G H
X Y

Oracle 11gR2 Setup:
CREATE TABLE table_name ( Col1, Col2 ) AS
SELECT 'A', 'B' FROM DUAL UNION ALL
SELECT 'B', 'C' FROM DUAL UNION ALL
SELECT 'E', 'F' FROM DUAL UNION ALL
SELECT 'F', 'G' FROM DUAL UNION ALL
SELECT 'G', 'H' FROM DUAL UNION ALL
SELECT 'X', 'Y' FROM DUAL;
Query:
SELECT SUBSTR( SYS_CONNECT_BY_PATH( Col1, ' ' ) || ' ' || Col2, 2 ) AS path
FROM table_name
WHERE CONNECT_BY_ISLEAF = 1
START WITH Col1 NOT IN ( SELECT Col2 FROM table_name )
CONNECT BY PRIOR Col2 = Col1;
Explanation:
Start (line 4) with each Col1 where there is not a parent row identified by a corresponding Col2 value and create a hierarchical query connecting (Line 5) Col1 the the prior parent row.
Filter the output only to those rows which are a leaf of the hierarchical tree (line 3) - i.e. those with no children.
You can then use SYS_CONNECT_BY_PATH to generate a string containing all the Col1 values from the root to the leaf of each branch of the tree generated by the hierarchy and concatenate that with the final Col2 value at the leaf. SUBSTR is used to remove the leading space delimiter that SYS_CONNECT_BY_PATH prepends to each entry in the path.
Output:
PATH
-------
A B C
E F G H
X Y

Is it what you search for?
SQL> with
2 src as (select 'A' p#, 'B' c# from dual union all
3 select 'B' p#, 'C' c# from dual union all
4 select 'E' p#, 'F' c# from dual union all
5 select 'F' p#, 'G' c# from dual union all
6 select 'G' p#, 'H' c# from dual union all
7 select 'X' p#, 'Y' c# from dual)
8 select
9 max(trim(sys_connect_by_path(p#, ' ') || ' ' || c#)) r#
10 from
11 src
12 start with
13 p# not in (select c# from src)
14 connect by p# = prior c#
15 group by connect_by_root(p#);
R#
--------------------------------------------------------------------------------
A B C
X Y
E F G H

May be this code can help you.
WITH t1(id, parent_id) AS (
-- Anchor member.
SELECT id,
PARENT
FROM table
WHERE id = 'A'
UNION ALL
-- Recursive member.
SELECT t2.id,
t2.PARENT
FROM table t2, table t1
WHERE t2.PARENT = t1.id
)
SELECT id, parent_id
FROM t1;

Related

SQL | SPLIT COLUMNS INTO ROWS

How can I split the column data into rows with basic SQL.
COL1 COL2
1 A-B
2 C-D
3 AAA-BB
Result
COL1 Col2
1 A
1 B
2 C
2 D
3 AAA
3 BB
From Oracle 12, if it is always two delimited values then you can use:
SELECT t.col1,
l.col2
FROM table_name t
CROSS JOIN LATERAL (
SELECT SUBSTR(col2, 1, INSTR(col2, '-') - 1) AS col2 FROM DUAL
UNION ALL
SELECT SUBSTR(col2, INSTR(col2, '-') + 1) FROM DUAL
) l
Which, for the sample data:
CREATE TABLE table_name (COL1, COL2) AS
SELECT 1, 'A-B' FROM DUAL UNION ALL
SELECT 2, 'C-D' FROM DUAL UNION ALL
SELECT 3, 'AAA-BB' FROM DUAL;
Outputs:
COL1
COL2
1
A
1
B
2
C
2
D
3
AAA
3
BB
db<>fiddle here
Snowflake is tagged, so here's the snowflake way of doing this:
WITH TEST (col1, col2) as
(select 1, 'A-B' from dual union all
select 2, 'C-D' from dual union all
select 3, 'AAA-BB' from dual
)
SELECT test.col1, table1.value
FROM test, LATERAL strtok_split_to_table(test.col2, '-') as table1
ORDER BY test.col1, table1.value;
As of Oracle:
SQL> with test (col1, col2) as
2 (select 1, 'A-B' from dual union all
3 select 2, 'C-D' from dual union all
4 select 3, 'AAA-BB' from dual
5 )
6 select col1,
7 regexp_substr(col2, '[^-]+', 1, column_value) col2
8 from test cross join
9 table(cast(multiset(select level from dual
10 connect by level <= regexp_count(col2, '-') + 1
11 ) as sys.odcinumberlist))
12 order by col1, col2;
COL1 COL2
---------- ------------------------
1 A
1 B
2 C
2 D
3 AAA
3 BB
6 rows selected.
SQL>
For MS-SQL 2016 and higher you can use:
SELECT Col1, x.value
FROM t CROSS APPLY STRING_SPLIT(t.Col2, '-') as x;
BTW: If Col2 contains null, it does not appear in the result.

sql placeholder rows

I have an apex item P_USERS which can have a value higher than the amount of rows returning from the query below.
I have a classic report which has the following query:
select
first_name,
last_name
from accounts
where account_role = 'Author'
order by account_nr;
I want placeholder rows to be added to the query (first_name = null, last_name = null etc.), if the total rows from the query is lesser than the value in the apex_item P_USERS.
Any tips on how to achieve this? Maybe with a LEFT join?
If you have more result than the minima you defined, you must add the rest with union.
Here is what you could try to adapt to your case:
SELECT i,c FROM (
select rownum i, c from (
select 'a' c from dual union all select 'b' from dual union all select 'd' from dual union all select 'be' from dual
)), (Select Rownum r From dual Connect By Rownum <= 3)
where (i(+)= r)
union select i,c from (select rownum i, c from (
select 'a' c from dual union all select 'b' from dual union all select 'd' from dual union all select 'be' from dual
)) where i>3
You may try to use a LEFT JOIN.
First, create a list of number until the limit you want like suggested here:
-- let's say you want 300 records
Select Rownum r From dual Connect By Rownum <= 300
Then you can use this to left join and have empty records:
SELECT C, R FROM
( select rownum i, c from (select 'a' c from dual union all select 'b' from dual) )
, ( Select Rownum r From dual Connect By Rownum <= 300)
where i(+)= r order by r
The above gives you an ordered list starting with 'a', 'b', then null until the end.
So you could adapt it to your case so:
SELECT F,L FROM
( select rownum i, f, l from (
select first_name f, last_name l
from accounts where account_role = 'Author'
order by account_nr) )
, ( Select Rownum r From dual Connect By Rownum <= 300)
where i(+)= r

Match characters in oracle sql where condition

I want to match Col1 from Table a to colum1 from table B.
A B
123 123-ab
234 234-bc
3443 3443-dd
However, value in table b has concatenated data. I want to match only the characters until before special character occurs(-).
I tried this : substr(table1.a,1,3) = substr(table2.b,1,3)
But this doesn’t work as some values have 4 digits.
use join and substr
select * from table_a
inner join table_b on table_a.col_a = substr(table_b.col_b, 1, length(table_a.col_a);
Using REGEXP_SUBSTR() to match on one or more numbers from the beginning of the string up to but not including the first hyphen:
SQL> with a(col1) as (
select '123' from dual union
select '234' from dual union
select '3443' from dual
),
b(col1) as (
select '123-ab' from dual union
select '234-bc' from dual union
select '3443-dd' from dual
)
select a.col1, b.col1
from a, b
where a.col1 = regexp_substr(b.col1, '^(\d+)-', 1, 1, NULL, 1);
COL1 COL1
---- -------
123 123-ab
234 234-bc
3443 3443-dd
SQL>

Oracle 9.2 pivot distinct value

The pivot function is available from Oracle 11 and i will need similar result using Oracle 9.2.
The main argument is that i need to pivot some values with a distinct result in a table like this:
id col3
1 a
1 b
--
2 a
2 a
2 b
--
3 a
3 b
3 c
My result sould be something like this
id a b c
1 1 1 0
2 1 1 0
3 1 1 1
To create a "manual" pivot i'm using a case/when but I am not able to understand how to get distinct value.
Right now the query is this:
with t as
( select 1 as id, 'a' as col1 from dual union all
select 1 as id, 'b' from dual union all
select 2 as id, 'a' from dual union all
select 2 as id, 'a' from dual union all
select 2 as id, 'b' from dual union all
select 3 as id, 'a' from dual union all
select 3 as id, 'b' from dual union all
select 3 as id, 'c' from dual
)
select t.id,
count(case when t.col1 = 'a' then 1 end) a,
count(case when t.col1 = 'b' then 1 end) b,
count(case when t.col1 = 'c' then 1 end) c
This produce correct value but obviously it just "count" the total a/b/c value and not the distinct.
thanks for the support
If I correctly understand your need, you can try something like the following; it aggregates by id and counts the distinct values of col3:
with t as
( select 1 as id, 'a' as col1 from dual union all
select 1 as id, 'b' from dual union all
select 2 as id, 'a' from dual union all
select 2 as id, 'a' from dual union all
select 2 as id, 'b' from dual union all
select 3 as id, 'a' from dual union all
select 3 as id, 'b' from dual union all
select 3 as id, 'c' from dual
)
select id,
count(distinct decode (col1, 'a', id, null)) a,
count(distinct decode (col1, 'b', id, null)) b,
count(distinct decode (col1, 'c', id, null)) c
from t
group by id
Of course the query depends on the number of different values of col3, but this is the same problem than pivot.

Oracle - Select all values from in clause even if there is no match

I have a dynamic data set such as 'AAA','TTT','CCC','FFF'
I need to match this data against a column C in a table T
e.g. I have in the table T for Column C, 'AAA','BBB','DDD','FFF'
I need to return something like (show null if the value doesn't exist in Column)
'AAA'
'TTT' NULL
'CCC' NULL
'FFF'
I don't want to drop the set into table as my data changes frequently and need to query quickly.
Any ideas greatly appreciated.
Is this what you're after ??
with w_data as (
select 'AAA' c from dual union all
select 'TTT' c from dual union all
select 'CCC' c from dual union all
select 'FFF' c from dual
),
w_table_t as (
select 'AAA' c from dual union all
select 'BBB' c from dual union all
select 'DDD' c from dual union all
select 'FFF' c from dual
)
select d.c,
NVL2(t.c, '', 'NULL' )
from w_data d
LEFT OUTER JOIN
w_table_t t
ON t.c = d.c
/
results:
C NVL2
--- ----
AAA
FFF
TTT NULL
CCC NULL
Try this (EDITED) :
with data_set as (
select 'AAA' col from dual union
select 'TTT' col from dual union
select 'CCC' col from dual union
select 'FFF' col from dual
)
select case when d.col in (select column_C from table_T) then d.col
else d.col||' Null' end as col_name
from data_set d
/

Resources