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