Recursive query in Oracle to find children and sibling - sql

I'm struggling a hierarchical SQL query. I want to have another 2 columns of the disp_order of its children and sibling.
Children - Should hold all disp_order of their child and their grand children and so far.
Sibling - Should hold the disp_order of the row having the same parent.
+------------+-----+-------------+--------+
| disp_order | lvl | description | parent |
+------------+-----+-------------+--------+
| 0 | 1 | A | |
| 1 | 2 | B | 0 |
| 2 | 3 | C | 1 |
| 3 | 4 | D | 2 |
| 4 | 5 | E | 3 |
| 5 | 2 | F | 0 |
| 6 | 3 | G | 5 |
| 7 | 3 | H | 5 |
| 8 | 3 | I | 5 |
| 9 | 4 | J | 8 |
| 10 | 5 | K | 9 |
+------------+-----+-------------+--------+
What the result should be:
+------------+-----+-------------+--------+------------------------+---------+
| disp_order | lvl | description | parent | children | sibling |
+------------+-----+-------------+--------+------------------------+---------+
| 0 | 1 | A | | 1,2,3,4,5,6,7,8,9,10 | |
| 1 | 2 | B | 0 | 2,3,4 | 5 |
| 2 | 3 | C | 1 | 3,4 | |
| 3 | 4 | D | 2 | 4 | |
| 4 | 5 | E | 3 | | |
| 5 | 2 | F | 0 | 6,7,8,9,10 | 1 |
| 6 | 3 | G | 5 | | 7,8 |
| 7 | 3 | H | 5 | | 6,8 |
| 8 | 3 | I | 5 | 9,10 | 6,7 |
| 9 | 4 | J | 8 | 10 | |
| 10 | 5 | K | 9 | | |
+------------+-----+-------------+--------+------------------------+---------+
Here is my current query:
SELECT t.*,
( SELECT MAX( disp_order )
FROM tbl_pattern p
WHERE p.lvl = t.lvl - 1
AND p.disp_order < t.disp_order ) AS parent
FROM tbl_pattern t

Continuing from your previous question:
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE tbl_pattern ( order_no, code, disp_order, lvl, description ) AS
SELECT 'RM001-01', 1, 0, 1, 'HK140904-1A' FROM DUAL UNION ALL
SELECT 'RM001-01', 1, 1, 2, 'HK140904-1B' FROM DUAL UNION ALL
SELECT 'RM001-01', 1, 2, 3, 'HK140904-1B' FROM DUAL UNION ALL
SELECT 'RM001-01', 1, 3, 4, 'HK140904-1C' FROM DUAL UNION ALL
SELECT 'RM001-01', 1, 4, 5, 'HK140904-1D' FROM DUAL UNION ALL
SELECT 'RM001-01', 1, 5, 2, 'HK140904-1E' FROM DUAL UNION ALL
SELECT 'RM001-01', 1, 6, 3, 'HK140904-1E' FROM DUAL UNION ALL
SELECT 'RM001-01', 1, 7, 3, 'HK140904-1X' FROM DUAL UNION ALL
SELECT 'RM001-01', 1, 8, 4, 'HK140904-1E' FROM DUAL UNION ALL
SELECT 'RM001-01', 1, 9, 5, 'HK140904-1E' FROM DUAL;
Query 1:
WITH data ( order_no, code, disp_order, lvl, description, parent ) AS (
SELECT t.*,
( SELECT MAX( disp_order )
FROM tbl_pattern p
WHERE p.order_no = t.order_no
AND p.code = t.code
AND p.lvl = t.lvl - 1
AND p.disp_order < t.disp_order ) AS parent
FROM tbl_pattern t
)
SELECT d.*,
( SELECT LISTAGG( c.disp_order, ',' ) WITHIN GROUP ( ORDER BY c.disp_order )
FROM data c
START WITH c.parent = d.disp_order
AND c.order_no = d.order_no
AND c.code = d.code
CONNECT BY PRIOR c.disp_order = c.parent
AND PRIOR c.order_no = c.order_no
AND PRIOR c.code = c.code
) AS children,
( SELECT LISTAGG( c.disp_order, ',' ) WITHIN GROUP ( ORDER BY c.disp_order )
FROM data c
WHERE c.parent = d.parent
AND c.disp_order <> d.disp_order
AND c.order_no = d.order_no
AND c.code = d.code
) AS siblings
FROM data d
Results:
| ORDER_NO | CODE | DISP_ORDER | LVL | DESCRIPTION | PARENT | CHILDREN | SIBLINGS |
|----------|------|------------|-----|-------------|--------|-------------------|----------|
| RM001-01 | 1 | 0 | 1 | HK140904-1A | (null) | 1,2,3,4,5,6,7,8,9 | (null) |
| RM001-01 | 1 | 1 | 2 | HK140904-1B | 0 | 2,3,4 | 5 |
| RM001-01 | 1 | 2 | 3 | HK140904-1B | 1 | 3,4 | (null) |
| RM001-01 | 1 | 3 | 4 | HK140904-1C | 2 | 4 | (null) |
| RM001-01 | 1 | 4 | 5 | HK140904-1D | 3 | (null) | (null) |
| RM001-01 | 1 | 5 | 2 | HK140904-1E | 0 | 6,7,8,9 | 1 |
| RM001-01 | 1 | 6 | 3 | HK140904-1E | 5 | (null) | 7 |
| RM001-01 | 1 | 7 | 3 | HK140904-1X | 5 | 8,9 | 6 |
| RM001-01 | 1 | 8 | 4 | HK140904-1E | 7 | 9 | (null) |
| RM001-01 | 1 | 9 | 5 | HK140904-1E | 8 | (null) | (null) |

Related

Get the count of longest streak including the break point

I am working on the problem where I have to get the count of streak with max value, but to get the exact result I have to count that point as well where the streak breaks. My table looks like this
+-----------------+--------+-------+
| customer_number | Months | Flags |
+-----------------+--------+-------+
| 1 | 12 | 1 |
| 1 | 1 | 1 |
| 1 | 2 | 1 |
| 1 | 3 | 1 |
| 1 | 4 | 1 |
| 1 | 5 | 1 |
| 1 | 8 | 1 |
| 1 | 9 | 1 |
| 1 | 10 | 1 |
| 1 | 11 | 1 |
| 6 | 12 | 1 |
| 6 | 1 | 1 |
| 6 | 2 | 1 |
| 6 | 3 | 1 |
| 6 | 4 | 1 |
| 6 | 5 | 4 |
| 6 | 9 | 1 |
| 6 | 10 | 1 |
| 6 | 11 | 1 |
| 7 | 5 | 1 |
| 8 | 9 | 1 |
| 8 | 10 | 1 |
| 8 | 11 | 1 |
| 9 | 9 | 1 |
| 9 | 10 | 1 |
| 9 | 11 | 1 |
| 10 | 11 | 1 |
+-----------------+--------+-------+
and my desired output is
+----------+--------------------+
| Customer | Consecutive streak |
+----------+--------------------+
| 1 | 10 |
| 6 | 6 |
| 7 | 1 |
| 8 | 3 |
| 9 | 3 |
| 10 | 1 |
+----------+--------------------+
the code I have
SELECT customer_number, max(streak) max_consecutive_streak FROM (
SELECT customer_number, COUNT(*) as streak
FROM
(select *,
(row_number() over (order by customer_number) -
row_number() over (order by customer_number)
) as counts
from table1
) cc
group by customer_number, counts
)
GROUP BY 1;
It is working good but for customer_number 6 it returns 5 but I want it to be 6, means it should count 4 as well in its longest streak as the streak breaks at this point. Any idea how can I achieve that?
You can use a cte with row_number:
with cte(r, id, flag) as (
select row_number() over (order by c.customer_number), c.* from customers c
),
freq(id, t, f) as (
select c2.id, c2.f, count(*) from
(select c.id, (select sum(c1.flag!=c.flag) from cte c1 where c1.id=c.id and c1.r <= c.r) f from cte c)
c2 group by c2.id, c2.f
)
select id, max(f) from freq group by id;

Use a Regex in SQL statement to extract several strings - Oracle

I want to extract some strings from the VAL column, according the regex furhter below in bold. This is an example of the data I have in source :
Table1
-----------------
ID VAL
-----------------
1 GR-RDE
2 GR-RZA-RDE
3 GR-RZA-RDE_RZA
4 GR-RGS
5 GR-RZA-OR-ORC
6 GR-RZA-RDE-OR-ORC_RZA
Desired result :
> Output
-----------------
ID RESULT
-----------------
1 RDE
2 RZA
2 RDE
3 RZA
3 RDE
4 RGS
5 RZA
5 OR
6 RZA
6 RDE
6 OR
To do that, I've done this regex :
(?<=-)(RDE|RZA|RGS|OR)(?![A-Z])
(?<=-) : checks that the character before is '-'
(RDE|RZA|RGS|OR) : search for 'RDE', 'RZA', 'RGS', 'OR' strings
(?![A-Z]) : ignore the string if it's followed by a letter
The regex works perfectly and it ignores all the unwhanted parts :
My problem is that I don't find the way to use this regex in a SQL statement (Oracle database). I've tried to perform a test with something like this, which returns Null :
select REGEXP_SUBSTR(VAL,'(?<=-)(RDE|RZA|RGS|OR)(?![A-Z])') from Table1;
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE Table1 ( ID, VAL ) AS
SELECT 1, 'GR-RDE' FROM DUAL UNION ALL
SELECT 2, 'GR-RZA-RDE' FROM DUAL UNION ALL
SELECT 3, 'GR-RZA-RDE_RZA' FROM DUAL UNION ALL
SELECT 4, 'GR-RGS' FROM DUAL UNION ALL
SELECT 5, 'GR-RZA-OR-ORC' FROM DUAL UNION ALL
SELECT 6, 'GR-RZA-RDE-OR-ORC_RZA' FROM DUAL
Query 1:
WITH words ( id, val, lvl, str, maxlvl ) AS (
SELECT id,
val,
1,
REGEXP_SUBSTR( val, '[A-Z]+', 1, 1 ),
REGEXP_COUNT( val, '[A-Z]+' )
FROM table1
UNION ALL
SELECT id,
val,
lvl + 1,
REGEXP_SUBSTR( val, '[A-Z]+', 1, lvl + 1 ),
maxlvl
FROM words
WHERE lvl < maxlvl
)
SELECT id, str, lvl
FROM words
ORDER BY id, lvl
Results:
| ID | STR | LVL |
|----|-----|-----|
| 1 | GR | 1 |
| 1 | RDE | 2 |
| 2 | GR | 1 |
| 2 | RZA | 2 |
| 2 | RDE | 3 |
| 3 | GR | 1 |
| 3 | RZA | 2 |
| 3 | RDE | 3 |
| 3 | RZA | 4 |
| 4 | GR | 1 |
| 4 | RGS | 2 |
| 5 | GR | 1 |
| 5 | RZA | 2 |
| 5 | OR | 3 |
| 5 | ORC | 4 |
| 6 | GR | 1 |
| 6 | RZA | 2 |
| 6 | RDE | 3 |
| 6 | OR | 4 |
| 6 | ORC | 5 |
| 6 | RZA | 6 |
Query 2:
SELECT t.id, w.COLUMN_VALUE AS str
FROM Table1 t
CROSS JOIN
TABLE(
CAST(
MULTISET(
SELECT REGEXP_SUBSTR( t.val, '[A-Z]+', 1, LEVEL )
FROM DUAL
CONNECT BY LEVEL <= REGEXP_COUNT( t.val, '[A-Z]+' )
) AS SYS.ODCIVARCHAR2LIST
)
) w
Results:
| ID | STR |
|----|-----|
| 1 | GR |
| 1 | RDE |
| 2 | GR |
| 2 | RZA |
| 2 | RDE |
| 3 | GR |
| 3 | RZA |
| 3 | RDE |
| 3 | RZA |
| 4 | GR |
| 4 | RGS |
| 5 | GR |
| 5 | RZA |
| 5 | OR |
| 5 | ORC |
| 6 | GR |
| 6 | RZA |
| 6 | RDE |
| 6 | OR |
| 6 | ORC |
| 6 | RZA |

Parent Child Hierarchy to Return All Descendants with Corresponding Primary ID

I have a parent child hierarchy table. I am trying to return a list of all of the child ID's for each child ID. My table is defined as follows:
CREATE TABLE Organization_Hierarchy_Test (ORGANIZATION_ID INT, PARENT_ORG_ID INT);
INSERT INTO Organization_Hierarchy_Test (ORGANIZATION_ID, PARENT_ORG_ID)
VALUES(1,0), (2,1), (3,1), (4,2), (5,2), (6,2), (7,3), (8,3), (9,3), (10,3);
The results that I am after would look like this:
+-----------------+---------------+--------------------------+
| ORGANIZATION_ID | PARENT_ORG_ID | ORIGINAL_ORGANIZATION_ID |
+-----------------+---------------+--------------------------+
| 1 | 0 | 1 |
| 2 | 1 | 1 |
| 3 | 1 | 1 |
| 4 | 2 | 1 |
| 5 | 2 | 1 |
| 6 | 2 | 1 |
| 7 | 3 | 1 |
| 8 | 3 | 1 |
| 9 | 3 | 1 |
| 10 | 3 | 1 |
| 2 | 0 | 2 |
| 3 | 0 | 2 |
| 4 | 1 | 2 |
| 5 | 1 | 2 |
| 6 | 1 | 2 |
| 7 | 1 | 2 |
| 8 | 1 | 2 |
| 9 | 1 | 2 |
| 10 | 1 | 2 |
| 4 | 0 | 4 |
| 5 | 0 | 4 |
| 6 | 0 | 4 |
| 7 | 0 | 4 |
| 8 | 0 | 4 |
| 9 | 0 | 4 |
| 10 | 0 | 4 |
+-----------------+---------------+--------------------------+
The query that I have written gets me a list of all of the descendants for each organization_id, but I can not figure out how to return the same organization_id that is in fact related to all of the descendants.
I have tried adding a group by and returning the max id with little luck. I have a delivery date tomorrow and I am worried that I am not going to be able to work through this in time.
with descendants as
( select PARENT_ORG_ID, ORGANIZATION_ID, 1 as level
from Organization_Hierarchy_Test OH
union all
select d.PARENT_ORG_ID , OH1.ORGANIZATION_ID, d.level + 1
from descendants as d
join Organization_Hierarchy_Test OH1 on d.ORGANIZATION_ID = OH1.PARENT_ORG_ID
)
select ORGANIZATION_ID, PARENT_ORG_ID, level
from descendants
order by level, PARENT_ORG_ID, ORGANIZATION_ID
Any ideas on how to return the original Organization_ID along with all of the descendant organization_id's?
I am trying to push this to a tabular model and this will save me loads of time in processing the data.
Thanks very much in advance.
Change your CTE to simply include an extra column d.ORGANIZATION_ID AS Orig:
with descendants as
( select PARENT_ORG_ID, ORGANIZATION_ID, ORGANIZATION_ID AS Orig, 1 as level
from Organization_Hierarchy_Test OH
union all
select d.PARENT_ORG_ID , OH1.ORGANIZATION_ID, d.ORGANIZATION_ID AS Orig, d.level + 1
from descendants as d
join Organization_Hierarchy_Test OH1 on d.ORGANIZATION_ID = OH1.PARENT_ORG_ID
)

Select 5 of each distinct value

I have the following table in PostgreSQL:
| a | b | c |
===================
| 'w' | 2 | 3 |
| 'w' | 7 | 2 |
| 'w' | 8 | 1 |
| 'w' | 3 | 6 |
| 'w' | 0 | 8 |
| 'w' | 2 | 9 |
| 'w' | 2 | 9 |
| 'z' | 4 | 9 |
| 'z' | 0 | 9 |
| 'z' | 0 | 8 |
| 'z' | 3 | 6 |
| 'z' | 2 | 7 |
| 'z' | 3 | 1 |
| 'z' | 3 | 2 |
| 'z' | 3 | 3 |
I want to select all records, but limit them to 5 records for each distinct value in column a.
So the result would look like:
| a | b | c |
===================
| 'w' | 2 | 3 |
| 'w' | 7 | 2 |
| 'w' | 8 | 1 |
| 'w' | 3 | 6 |
| 'w' | 0 | 8 |
| 'z' | 4 | 9 |
| 'z' | 0 | 9 |
| 'z' | 0 | 8 |
| 'z' | 3 | 6 |
| 'z' | 2 | 7 |
What is the most effecient way to achieve that in RoR? Thanks!
you can use row_number, but you have to specify order or you will get unpredictable resutls
with cte as (
select
*,
row_number() over(partition by a order by b, c) as row_num
from table1
)
select a, b, c
from cte
where row_num <= 5

Crosstab multi columns

Hello I have a problem with SQL in SQL Server 2005.
Suppose that I have a table called myTable with data as below:
| NAME | CREDIT | GRADE | YEAR | SEMESTER |
---------------------------------------------
| Name1 | 1 | A | 1 | 1 |
| Name2 | 4 | B | 1 | 1 |
| Name3 | 2 | E | 1 | 1 |
| Name4 | 7 | F | 1 | 1 |
| Name5 | 4 | A | 1 | 2 |
| Name6 | 3 | C | 1 | 2 |
| Name7 | 6 | D | 1 | 2 |
| Name8 | 1 | A | 1 | 2 |
| Name9 | 1 | A | 1 | 2 |
| Name10 | 1 | A | 1 | 2 |
| Name11 | 3 | C | 2 | 1 |
| Name12 | 6 | E | 2 | 1 |
| Name13 | 4 | C | 2 | 1 |
| Name14 | 2 | B | 2 | 2 |
| Name15 | 1 | A | 2 | 2 |
| Name16 | 1 | A | 2 | 2 |
| Name17 | 1 | A | 2 | 2 |
| Name18 | 5 | D | 3 | 1 |
| Name19 | 1 | A | 3 | 1 |
| Name20 | 1 | A | 3 | 1 |
| Name18 | 5 | D | 3 | 2 |
| Name19 | 1 | A | 3 | 2 |
| Name20 | 1 | A | 3 | 2 |
I want to output the result as below:
| NAM1 | CRDT1 | GRD1 | YEAR1 | SEMER1 | NAM2 | CRDT2 | GRD2 | YEAR2 | SEMES2 |
-----------------------------------------------------------------------------
| Name1| 1 | A | 1 | 1 |Name5 | 4 | A | 1 | 2 |
| Name2| 4 | B | 1 | 1 |Name6 | 3 | C | 1 | 2 |
| Name3| 2 | E | 1 | 1 |Name7 | 6 | D | 1 | 2 |
| Name4| 7 | F | 1 | 1 |Name8 | 1 | A | 1 | 2 |
|Name9 | 1 | A | 1 | 2 |
|Name10| 1 | A | 1 | 2 |
| Name11| 3 | C | 2 | 1 |Name14| 2 | B | 2 | 2 |
| Name12| 6 | E | 2 | 1 |Name15| 1 | A | 2 | 2 |
| Name13| 4 | C | 2 | 1 |Name16| 1 | A | 2 | 2 |
|Name17| 1 | A | 2 | 2 |
| Name18| 5 | D | 3 | 1 |Name18| 5 | D | 3 | 2 |
| Name19| 1 | A | 3 | 1 |Name19| 1 | A | 3 | 2 |
| Name20| 1 | A | 3 | 1 |Name20| 1 | A | 3 | 2 |
Where
- Nam1= Name in Semester 1
- CRDT1= Credit in Semester 1
- GRD1= Grade in Semester 1
- Year1= Year in Semester 1
- Semer1 = Semester in Semester 1
- Nam2= Name in Semester 2
- CRDT2= Credit in Semester 2
- GRD2= Grade in Semester 2
- Year2= Year in Semester 2
- Semer2 = Semester in Semester 2
Please go to this URL to test this SQL: http://sqlfiddle.com/#!3/196c6/1
How Can I create SQL to make output like this?
select
s1.Name as nam1, s1.credit as crdt1, s1.Year as year1, s1.semester as semer1,
s2.Name as nam2, s2.credit as crdt2, s2.Year as year2, s2.semester as semer2
from
(select *, ROW_NUMBER() over (partition by year order by name) rn from myTable where semester=1 ) s1
full outer join
(select *, ROW_NUMBER() over (partition by year order by name) rn from myTable where semester=2 ) s2
on s1.year = s2.year
and s1.rn = s2.rn
I don't like doing an outer join, when a simple group by is sufficient:
select max(case when semester = 1 then Name end) as name1,
max(case when semester = 1 then credit end) as credit1,
max(case when semester = 1 then year end) as year1,
max(case when semester = 1 then semester end) as semester1,
max(case when semester = 2 then Name end) as name2,
max(case when semester = 2 then credit end) as credit2,
max(case when semester = 2 then year end) as year2,
max(case when semester = 2 then semester end) as semester2
from (select t.*,
row_number() over (partition by semester order by name) as rownum
from t
) t2
group by rownum
order by rownum
select Name,credit, grade, year,semester from myTable
group by semester,year, Name,credit, grade;
now we have to make a dynamic query with this previous query:
create as temporary table as there are semster first
create dynamically a select query with all fields of all semester table in a loop:
foreach temporary table concat all fields of this table in select query
and add construct label field with semester value of this table
and add temporary table with union
'select' + #tbls1.fieldName + ',' + ... + + #tbls2.fieldName +