PL Sql Creating Hierarcical ID - sql

i have this table without any hierarcy but its obvious it has a hierarcy like 100,100-01,100-01-01 ..
ID ACCOUNT
3 100
4 100-01
5 100-01-01
6 100-01-01-001
7 100-01-01-006
8 100-01-01-271
9 100-01-01-272
10 100-01-01-273
11 100-01-16
12 100-01-16-420
13 100-02
14 100-02-01
15 100-02-01-001
16 100-02-01-271
17 100-02-01-272
i want to fill like this; 100-01 is child of 100 , 100-01-01 is child of 100-01 so ;
ID H_ID ACCOUNT
3 100
4 3 100-01
5 4 100-01-01
6 5 100-01-01-001
7 5 100-01-01-006
8 5 100-01-01-271
9 5 100-01-01-272
10 5 100-01-01-273
11 4 100-01-16
12 11 100-01-16-420
13 3 100-02
14 13 100-02-01
15 14 100-02-01-001
16 14 100-02-01-271
17 14 100-02-01-272
Thank you for your time...

You can find the 'parent' account with substr and instr:
select id, account, substr(account, 1, instr(account, '-', -1, 1) - 1)
from your_table;
ID ACCOUNT SUBSTR(ACCOUN
---------- ------------- -------------
3 100
4 100-01 100
5 100-01-01 100-01
6 100-01-01-001 100-01-01
...
You can then (outer) join the table to itself using that value:
select t1.id, t2.id as h_id, t1.account
from your_table t1
left join your_table t2
on t2.account = substr(t1.account, 1, instr(t1.account, '-', -1, 1) - 1)
order by t1.id;
which with your sample data gives:
ID H_ID ACCOUNT
---------- ---------- -------------
3 100
4 3 100-01
5 4 100-01-01
6 5 100-01-01-001
7 5 100-01-01-006
8 5 100-01-01-271
9 5 100-01-01-272
10 5 100-01-01-273
11 4 100-01-16
12 11 100-01-16-420
13 3 100-02
14 13 100-02-01
15 14 100-02-01-001
16 14 100-02-01-271
17 14 100-02-01-272
There is no parent for ID 3 so it isn't clear why your expected result has 2 for that.
You could also extract the 'branch' and 'leaf' parts of the account if that's useful:
select t1.id, t2.id as h_id, t1.account,
substr(t1.account, 1, instr(t1.account, '-', -1, 1) - 1) as branch,
substr(t1.account, instr(t1.account, '-', -1, 1) + 1) as leaf
from your_table t1
left join your_table t2
on t2.account = substr(t1.account, 1, instr(t1.account, '-', -1, 1) - 1)
order by t1.id;
ID H_ID ACCOUNT BRANCH LEAF
---------- ---------- ------------- ------------- -------------
3 100 100
4 3 100-01 100 01
5 4 100-01-01 100-01 01
6 5 100-01-01-001 100-01-01 001
7 5 100-01-01-006 100-01-01 006
8 5 100-01-01-271 100-01-01 271
...
db<>fiddle
In principle, if this is a real hierarchy, you only need to store the ID, parent ID and leaf part, and can then reconstruct the whole account with a hierarchical query (or recursive CTE). That may not actually be practical, or sensible, for your situation.
Looking at the edit history of your question, it seems like your table has an empty h_id column that you want to populate. You can use the same mechanism with a correlated update:
update your_table t1
set h_id = (
select t2.id
from your_table t2
where t2.account = substr(t1.account, 1, instr(t1.account, '-', -1, 1) - 1)
);
db<>fiddle

Related

Generate series of integers with random start and end SQL Oracle

I am trying to generate a series of integers that go from a random start-point until a random (greater) end-point. Furthermore, I would like to do this as a window function, so I could use this in a OVER (PARTITION BY ) statement.
Basically I'm trying to select random hours (from 1-24), but in a random way and also consecutively, and do this for each client (which is why I suggest an OVER (PARTITION BY client) statement, but I'm open for other ideas.
I am trying to use:
SELECT
T1.HOURS
FROM (
SELECT
LEVEL HOURS
FROM DUAL
CONNECT BY
LEVEL <= 24
) T1,
(
SELECT
INIT,
LEAST(INIT + LENGTH, 24) FIN
FROM (
SELECT
ROUND(DBMS_RANDOM.VALUE(1, 24)) INIT,
ROUND(DBMS_RANDOM.VALUE(1, 24)) LENGTH
FROM DUAL
) T0
) T2
WHERE
T1.HOURS >= T2.INIT AND
T1.HOURS <= T2.FIN;
But the result is unfortunately non-consecutive orders.
|hours|
|-----|
|17|
|18|
|20|
|24|
The code is simply nor working because the table T0 (which is the one that filters the initial INIT and final FIN value) is being replicated for each row.
This is a desired result:
client
hours
1
4
1
5
1
6
1
7
2
14
2
15
3
13
3
14
3
15
3
16
3
17
3
18
3
19
3
20
3
21
In the desired result a consecutive list is selected for each client, with a random start and end point.
In Oracle, you can use a correlated LATERAL join:
SELECT c.id,
h.hours
FROM ( SELECT id,
FLOOR(DBMS_RANDOM.VALUE(1, 25)) AS bound1,
FLOOR(DBMS_RANDOM.VALUE(1, 25)) AS bound2
FROM clients
) c
CROSS JOIN LATERAL (
SELECT LEAST(bound1, bound2) + LEVEL - 1 AS hours
FROM DUAL
CONNECT BY LEVEL <= ABS(bound1 - bound2) + 1
) h;
Then, for the sample data:
CREATE TABLE clients (id) AS
SELECT LEVEL FROM DUAL CONNECT BY LEVEL <= 3;
May (randomly) output:
ID
HOURS
1
7
1
8
1
9
1
10
2
19
2
20
2
21
2
22
2
23
2
24
3
6
3
7
3
8
3
9
3
10
3
11
3
12
3
13
3
14
3
15
3
16
fiddle

SQL: subset data: select id when time_id for id satisfy a condition from another column

I have a data (dt) in SQL like the following:
ID time_id act rd
11 1 1 1
11 2 4 1
11 3 7 0
12 1 8 1
12 2 2 0
12 3 4 1
12 4 3 1
12 5 4 1
13 1 4 1
13 2 1 0
15 1 3 1
16 1 8 0
16 2 8 0
16 3 8 0
16 4 8 0
16 5 8 0
and I want to take the subset of this data such that only ids (and their corresponding time_id, act, rd) that has time_id == 5 is retained. The desired output is the following
ID time_id act rd
12 1 8 1
12 2 2 0
12 3 4 1
12 4 3 1
12 5 4 1
16 1 8 0
16 2 8 0
16 3 8 0
16 4 8 0
16 5 8 0
I know I should use having clause somehow but have not been successful so far (returns me empty outputs). below is my attempt:
SELECT * FROM dt
GROUP BY ID
Having min(time_id) == 5;
This query:
select id from tablename where time_id = 5
returns all the ids that you want in the results.
Use it with the operator IN:
select *
from tablename
where id in (select id from tablename where time_id = 5)
You can use a correlated subquery with exists:
select t.*
from t
where exists (select 1 from t t2 where t2.id = t.id and t2.time_id = 5);
WITH temp AS
(
SELECT id FROM tab WHERE time_id = 5
)
SELECT * FROM tab t join temp tp on(t.id=tp.id);
check this query
select * from table t1 join (select distinct ID from table t where time_id = 5) t2 on t1.id =t2.id;

SQL Recursive CTE unexpectedly returns alternating sets

I am trying to get the use recursive CTE to repeat the same pattern over and over, resetting when "Scenario" increases in value. RowNumber repeats 1-21 (as desired), but whenever "Scenario" is an even number, there are too few items in the "Vals" column to feed into "Value". I can't figure out which part of the code is causing me to be 1 short for only even Scenarios.
Below are the results of the code I'm using at the bottom.
Scenario RowNumber Value Vals
1 1 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,C
1 2 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,C
1 3 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,C
1 4 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,C
1 5 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,C
1 6 A A,A,A,A,A,A,A,A,A,A,A,A,A,B,C
1 7 A A,A,A,A,A,A,A,A,A,A,A,A,B,C
1 8 A A,A,A,A,A,A,A,A,A,A,A,B,C
1 9 A A,A,A,A,A,A,A,A,A,A,B,C
1 10 A A,A,A,A,A,A,A,A,A,B,C
1 11 A A,A,A,A,A,A,A,A,B,C
1 12 A A,A,A,A,A,A,A,B,C
1 13 A A,A,A,A,A,A,B,C
1 14 A A,A,A,A,A,B,C
1 15 A A,A,A,A,B,C
1 16 A A,A,A,B,C
1 17 A A,A,B,C
1 18 A A,B,C
1 19 A B,C
1 20 B C
1 21 C
2 1 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,B,C
2 2 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,B,C
2 3 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,B,C
2 4 A A,A,A,A,A,A,A,A,A,A,A,A,A,B,B,C
2 5 A A,A,A,A,A,A,A,A,A,A,A,A,B,B,C
2 6 A A,A,A,A,A,A,A,A,A,A,A,B,B,C
2 7 A A,A,A,A,A,A,A,A,A,A,B,B,C
2 8 A A,A,A,A,A,A,A,A,A,B,B,C
2 9 A A,A,A,A,A,A,A,A,B,B,C
2 10 A A,A,A,A,A,A,A,B,B,C
2 11 A A,A,A,A,A,A,B,B,C
2 12 A A,A,A,A,A,B,B,C
2 13 A A,A,A,A,B,B,C
2 14 A A,A,A,B,B,C
2 15 A A,A,B,B,C
2 16 A A,B,B,C
2 17 A B,B,C
2 18 B B,C
2 19 B C
2 20 C
2 21 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,B,C
3 1 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,C,C
3 2 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,C,C
3 3 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,C,C
3 4 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,C,C
3 5 A A,A,A,A,A,A,A,A,A,A,A,A,A,B,C,C
3 6 A A,A,A,A,A,A,A,A,A,A,A,A,B,C,C
3 7 A A,A,A,A,A,A,A,A,A,A,A,B,C,C
3 8 A A,A,A,A,A,A,A,A,A,A,B,C,C
3 9 A A,A,A,A,A,A,A,A,A,B,C,C
3 10 A A,A,A,A,A,A,A,A,B,C,C
3 11 A A,A,A,A,A,A,A,B,C,C
3 12 A A,A,A,A,A,A,B,C,C
3 13 A A,A,A,A,A,B,C,C
3 14 A A,A,A,A,B,C,C
3 15 A A,A,A,B,C,C
3 16 A A,A,B,C,C
3 17 A A,B,C,C
3 18 A B,C,C
3 19 B C,C
3 20 C C
3 21 C
4 1 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,B,B,C
4 2 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,B,B,C
4 3 A A,A,A,A,A,A,A,A,A,A,A,A,A,B,B,B,C
4 4 A A,A,A,A,A,A,A,A,A,A,A,A,B,B,B,C
4 5 A A,A,A,A,A,A,A,A,A,A,A,B,B,B,C
4 6 A A,A,A,A,A,A,A,A,A,A,B,B,B,C
4 7 A A,A,A,A,A,A,A,A,A,B,B,B,C
4 8 A A,A,A,A,A,A,A,A,B,B,B,C
4 9 A A,A,A,A,A,A,A,B,B,B,C
4 10 A A,A,A,A,A,A,B,B,B,C
4 11 A A,A,A,A,A,B,B,B,C
4 12 A A,A,A,A,B,B,B,C
4 13 A A,A,A,B,B,B,C
4 14 A A,A,B,B,B,C
4 15 A A,B,B,B,C
4 16 A B,B,B,C
4 17 B B,B,C
4 18 B B,C
4 19 B C
4 20 C
This is the code I used to generate the above sample. Where am I going wrong?
CREATE TABLE #temp3
(
Scenario INT
,Vals VARCHAR(64)
,LEN INT
)
;
WITH vals AS
(
SELECT
v.*
FROM
(VALUES ('A'), ('B'), ('C')) v(x)
),
CTE AS
(
SELECT CAST('A' AS VARCHAR(MAX)) AS STR, 0 AS LEN
UNION ALL
SELECT (CTE.STR + ',' + vals.x), CTE.LEN + 1
FROM
CTE
JOIN vals
ON vals.x >= RIGHT(CTE.STR, 1)
WHERE CTE.LEN < 19
)
INSERT INTO #temp3
SELECT
ROW_NUMBER() OVER(ORDER BY STR + ',C') AS Scenario
,STR + ',C' AS Vals
,LEN
FROM
CTE
WHERE
STR + 'C' LIKE '%B%'
AND LEN = 19
;
-- Split strings created above into individual characters
WITH cte(Scenario, Value, Vals) AS
(
SELECT
Scenario
,CAST(LEFT(Vals, CHARINDEX(',',Vals+',')-1) AS VARCHAR(10)) AS Value
,STUFF(Vals, 1, CHARINDEX(',',Vals+','), '') AS Vals
FROM #temp3
UNION ALL
SELECT
Scenario
,CAST(LEFT(Vals, CHARINDEX(',',Vals+',')-1) AS VARCHAR(10))
,STUFF(Vals, 1, CHARINDEX(',',Vals+','), '')
FROM cte
WHERE Vals > ''
)
SELECT
Scenario
,ROW_NUMBER() OVER (PARTITION BY Scenario ORDER BY Scenario) RowNumber
,Value
,Vals
FROM cte t
I'm not exactly sure what the problem you are describing is, but the ROW_NUMBER() should use an ORDER BY clause that completely orders the rows in each partition.
When you use "PARTITION BY Scenario ORDER BY Scenario" the order in which the ROW_NUMBER() values are assigned is undefined. Try something like
WITH cte(Scenario, depth, Value, Vals) AS
(
SELECT
Scenario, 0 depth
,CAST(LEFT(Vals, CHARINDEX(',',Vals+',')-1) AS VARCHAR(10)) AS Value
,STUFF(Vals, 1, CHARINDEX(',',Vals+','), '') AS Vals
FROM #temp3
UNION ALL
SELECT
Scenario, depth+1
,CAST(LEFT(Vals, CHARINDEX(',',Vals+',')-1) AS VARCHAR(10))
,STUFF(Vals, 1, CHARINDEX(',',Vals+','), '')
FROM cte
WHERE Vals > ''
)
SELECT
Scenario
,depth
,ROW_NUMBER() OVER (PARTITION BY Scenario ORDER BY depth ) RowNumber
,Value
,Vals
FROM cte t

Sum Amount, display full resultset where Groups of Column Values Match in Same Table Oracle SQL

I need to get the sum of all TOTAL fields where the ID, RECNO and DRCR fields have the same value, while also displaying these fields in the result set.
eg
ID RECNO SECRECNO DRCR TOTAL
1 9 5 D 25
1 9 12 D 22
1 9 6 C 33
1 9 5 D 50
1 8 2 D 12
1 8 2 C 23
2 9 5 D 100
So the results of the query should be
ID RECNO SECRECNO DRCR SUM(TOTAL)
1 9 5 D 75
1 9 12 D 22
1 9 6 C 33
1 8 2 D 12
1 8 2 C 23
2 9 5 D 100
This query will give the results set, without the TOTAL:
select distinct t1.recno, t1.secrecno
from table t1, table t2
where t1.recno = t2.recno and t.id = '1' and t1.drcr = 'D'
But I can't see how to SUM the TOTAL of these results.
How to do this?
select t1.id,
t1.recno,
t1.secrecno,
t1.drcr,
SUM( TOTAL )
from table t1
INNER JOIN
table t2
ON ( t1.recno = t2.recno )
WHERE t1.id = '1'
AND t1.drcr = 'D'
GROUP BY
t1.id,
t1.recno,
t1.secrecno,
t1.drcr

Returning a list of rows that are unique by type and returning the first pass

ID UserID TYPE PASS DATE
1 12 TRACK1 1 20140101
2 32 TRACK2 0 20140105
3 43 PULL1 1 20140105
4 66 PULL2 1 20140110
5 54 PULL1 0 20140119
6 54 TRACK1 0 20140120
So users can take multiple attempts for 'Type', so they can take 'TRACK1' multiple times, or 'PULL2' multiple times.
I want to return the first PASS (1) for each unique 'Type' for each user.
I want to return both pass and fail rows, but only the first instance of a pass or fail.
How can I do this?
sample table and output
ID UserID TYPE PASS DATE
1 12 TRACK1 1 20140101
2 12 TRACK2 0 20140105
3 12 PULL1 1 20140105
4 12 PULL2 1 20140110
5 12 PULL1 0 20140119
6 12 TRACK1 0 20140120
7 12 TRACK1 0 20140121
8 12 PULL1 1 20140115
9 12 TRACK2 0 20140125
output:
1 12 TRACK1 1 20140101
2 12 TRACK2 0 20140105
3 12 PULL1 1 20140105
4 12 PULL2 1 20140110
select t1.*
from UserTrackStatus t1
join
(
select userid,
type,
min(date) as min_date
from UserTrackStatus
group by userid, type
) t2 on t1.userid = t2.userid and t1.type = t2.type and t1.date = t2.min_date
SQLFiddle
Just do it with CTE and ROW_NUMBER to identify which records comes first
;
WITH cte
AS (
SELECT *
,ROW_NUMBER() OVER ( PARTITION BY [UserID], [Type] ORDER BY [date] ASC ) AS rn
FROM MyTable
WHERE PASS = 1
)
SELECT *
FROM cte
WHERE rn = 1