Get Ultimate manager oracle - oracle

I have data in the below format.
Emp to_location from_location Vehicle
---------------------------------------------
1 A B Road
1 B C Ship
1 C D Air
1 X D Bus
Need the output as
Emp ToL FromL Vehicle
--------------------------
1 A D Air
I tried using Connect by and Start with but the result is coming up as below.
Emp FromL ToL Path
--------------------------
1 B C Air
I need the output as
Emp FromL ToL Path
--------------------------
1 D A Air
The query I have built is like below.
with t as
( select 1 emp, 'A' tloc, 'B' floc, 'Road' v from dual union all
select 1 emp,'B' tloc, 'C' floc, 'Ship' v from dual union all
select 1 emp,'C' tloc, 'D' floc, 'Air' v from dual union all
select 1 emp,'X' tloc, 'D' floc, 'Bus' v from dual
)
select emp,
connect_by_root floc from_loc,
tloc to_location,
v,
CONNECT_BY_ISLEAF ch
from T
where CONNECT_BY_ISLEAF=1
CONNECT BY nocycle prior floc= tloc and prior emp=emp
AND PRIOR SYS_GUID() IS NOT NULL
START WITH tloc ='A'
Can anyone correct the minor thing that I am missing to get the correct output? TIA

I think your query is fine - you just to pick up the root element as you go
SQL> with t as
2 ( select 1 emp, 'A' floc, 'B' tloc, 'Road' v from dual union all
3 select 1 emp,'B' floc, 'C' tloc, 'Ship' v from dual union all
4 select 1 emp,'C' floc, 'D' tloc, 'Air' v from dual union all
5 select 1 emp,'X' floc, 'D' tloc, 'Bus' v from dual
6 )
7 select t.*, level
8 from T
9 where emp=1
10 CONNECT BY nocycle floc= PRIOR tloc and prior emp=emp
11 START WITH floc ='A';
EMP F T V LEVEL
---------- - - ---- ----------
1 A B Road 1
1 B C Ship 2
1 C D Air 3
to then yield
SQL> with t as
2 ( select 1 emp, 'A' floc, 'B' tloc, 'Road' v from dual union all
3 select 1 emp,'B' floc, 'C' tloc, 'Ship' v from dual union all
4 select 1 emp,'C' floc, 'D' tloc, 'Air' v from dual union all
5 select 1 emp,'X' floc, 'D' tloc, 'Bus' v from dual
6 )
7 select emp,
8 connect_by_root floc from_loc,
9 tloc to_location,
10 ltrim(sys_connect_by_path(v,'-'),'-') path
11 from T
12 where emp=1 and CONNECT_BY_ISLEAF=1
13 CONNECT BY nocycle floc= PRIOR tloc and prior emp=emp
14 START WITH floc ='A';
EMP F T PATH
---------- - - ------------------------------
1 A D Road-Ship-Air

Related

oracle sql split string to rows (alphabet in sequential order) - Oracle SQL

I am looking for a oracle sql solution for the below problem:
Here's one option (as union of 3 different cases).
Sample data:
SQL> with test (col) as
2 (select 'A-D' from dual union all
3 select 'J-K' from dual union all
4 select 'X-20' from dual union all
5 select 'XX-20542' from dual union all
6 select 'A/B' from dual union all
7 select 'J/K' from dual
8 )
Query begins here:
9 -- A-D option:
10 select col,
11 chr(ascii(substr(col, 1, 1)) + column_value - 1) res
12 from test cross join
13 table(cast(multiset(select level from dual
14 connect by level <= ascii(substr(col, -1)) - ascii(substr(col, 1, 1)) + 1
15 ) as sys.odcinumberlist))
16 where regexp_like(col, '[[:alpha:]]-[[:alpha:]]')
17 -- XX-20542 option:
18 union all
19 select col, 'No action / ignore'
20 from test
21 where regexp_like(col, '[[:alpha:]]+-\d+')
22 -- A/B option:
23 union all
24 select col,
25 regexp_substr(col, '[[:alpha:]]', 1, column_value)
26 from test cross join
27 table(cast(multiset(select level from dual
28 connect by level <= ascii(substr(col, -1)) - ascii(substr(col, 1, 1)) + 1
29 ) as sys.odcinumberlist))
30 where regexp_like(col, '[[:alpha:]]/[[:alpha:]]');
Result:
COL RES
-------- ------------------
A-D A
A-D B
A-D C
A-D D
J-K J
J-K K
X-20 No action / ignore
XX-20542 No action / ignore
A/B A
A/B B
J/K J
J/K K
12 rows selected.
SQL>
You can use a single recursive query:
WITH ranges (value, min_value, skip, max_value) AS (
SELECT value,
REGEXP_SUBSTR(value, '^([A-Z])([[:punct:]])([A-Z])$', 1, 1, 'i', 1),
CASE REGEXP_SUBSTR(value, '^([A-Z])([[:punct:]])([A-Z])$', 1, 1, 'i', 2)
WHEN '-'
THEN 0
ELSE 1
END,
REGEXP_SUBSTR(value, '^([A-Z])([[:punct:]])([A-Z])$', 1, 1, 'i', 3)
FROM table_name
UNION ALL
SELECT value,
CASE skip
WHEN 0
THEN CHR(ASCII(min_value) + 1)
ELSE max_value
END,
skip,
max_value
FROM ranges
WHERE min_value < max_value
)
SEARCH DEPTH FIRST BY value SET rn
SELECT value,
COALESCE(min_value, 'ignore') AS result
FROM ranges
Which, for the sample data:
CREATE TABLE table_name (value) AS
SELECT 'A-D' FROM DUAL UNION ALL
SELECT 'J-K' FROM DUAL UNION ALL
SELECT 'XX-Y' FROM DUAL UNION ALL
SELECT 'Y-ZZ' FROM DUAL UNION ALL
SELECT 'B/C' FROM DUAL UNION ALL
SELECT 'E/E' FROM DUAL UNION ALL
SELECT 'F/H' FROM DUAL;
Outputs:
VALUE
RESULT
A-D
A
A-D
B
A-D
C
A-D
D
B/C
B
B/C
C
E/E
E
F/H
F
F/H
H
J-K
J
J-K
K
XX-Y
ignore
Y-ZZ
ignore
db<>fiddle here

oracle ROW_NUMBER()

I would like to write a query to display this kind of results
column0
column1
column2
column3
a
x
1
1
a
y
1
2
a
z
1
3
b
x
2
1
b
y
2
2
c
x
3
1
c
y
3
2
c
z
3
3
column2 -> row number of column0
column3 -> row number of partition column0 & column1
I have tried this but not working
SELECT ROW_NUMBER() OVER (PARTITION BY column0 ORDER BY column0 ) column2,
ROW_NUMBER() OVER (PARTITION BY column0 , column1 ORDER BY column0 , column1) column3
FROM DUAL
have you got any ideas ?
Use DENSE_RANK for column2 and ROW_NUMBER for column3:
SELECT
column0,
column1,
DENSE_RANK() OVER (ORDER BY column0) AS column3,
ROW_NUMBER() OVER (PARTITION BY column0 ORDER BY column1) AS column4
FROM yourTable;
With row_number:
SQL> with test (col0, col1) as
2 (select 'a', 'x' from dual union all
3 select 'a', 'y' from dual union all
4 select 'a', 'z' from dual union all
5 select 'b', 'x' from dual union all
6 select 'b', 'y' from dual
7 )
8 select col0, col1,
9 row_number() over (partition by col1 order by col0) col2,
10 row_number() over (partition by col0 order by col1) col3
11 from test
12 order by col0, col1
13 /
COL0 COL1 COL2 COL3
----- ------ ---------- ----------
a x 1 1
a y 1 2
a z 1 3
b x 2 1
b y 2 2
SQL>

Oracle: How functional table works in cross join?

I explored technique to unfold comma separated list in column into rows:
with tbl as (
select 1 id, 'a,b' lst from dual
union all select 2 id, 'c' lst from dual
union all select 3 id, 'e,f,g' lst from dual)
select
tbl.ID
, regexp_substr(tbl.lst, '[^,]+', 1, lvl.column_value) elem
, lvl.column_value lvl
from
tbl
, table(cast(multiset(
select level from dual
connect by level <= regexp_count(tbl.lst, ',')+1) as sys.odcinumberlist)) lvl;
Result is:
ID ELEM LVL
1 a 1
1 b 2
2 c 1
3 e 1
3 f 2
3 g 3
As you can see LVL depends on value of regexp_count, so second functional table in cross join is parametrized by first table.
How is it working? How is it called? Can I paramertize third table based on two preceding in cross join and so forth?
Is parametrization limited to cross join or can be applied in join syntax too?
Reference: Splitting string into multiple rows in Oracle
From the documentation:
LATERAL
Specify LATERAL to designate subquery as a lateral inline
view. Within a lateral inline view, you can specify tables that appear
to the left of the lateral inline view in the FROM clause of a query.
You can specify this left correlation anywhere within subquery (such
as the SELECT, FROM, and WHERE clauses) and at any nesting level.
-- a variation of the query in your question ...
select
dt.id
, dt.list
, regexp_substr( dt.list, '[^,]+', 1, dt2.lvl ) elements
, dt2.lvl
from (
select 1 id, 'a,b' list from dual union all
select 2, 'c' from dual union all
select 3, 'e,f,g' from dual
) dt, lateral (
select level lvl from dual
connect by level <= regexp_count(dt.list, ',') + 1
) dt2
;
-- output
ID LIST ELEMENTS LVL
1 a,b a 1
1 a,b b 2
2 c c 1
3 e,f,g e 1
3 e,f,g f 2
3 e,f,g g 3
Example with 3 tables:
--drop table t1 ;
--drop table t2 ;
--drop table t3 ;
-- tables/data
create table t1
as
select 1 id, 'a' letter from dual union all
select 2, 'b' from dual union all
select 3, 'c' from dual ;
create table t2
as
select 1 id, 'd' letter from dual union all
select 2, 'e' from dual union all
select 3, 'f' from dual ;
create table t3
as
select 1 id, 'g' letter from dual union all
select 2, 'h' from dual union all
select 3, 'i' from dual ;
-- query
select *
from
t1
, lateral ( select letter from t2 where id = t1.id ) t2
, lateral ( select letter from t3 where id = t2.id )
;
-- output
ID LETTER LETTER LETTER
1 a d g
2 b e h
3 c f i
Also (using the same tables)
-- reference t1 <- t2,
-- reference t1 and t2 <- t3
select *
from
t1
, lateral ( select letter from t2 where id = t1.id ) t2
, lateral ( select letter || t1.letter from t3 where id = t2.id )
;
-- output
ID LETTER LETTER LETTER||T1.LETTER
1 a d ga
2 b e hb
3 c f ic
Whereas a "standard" cross join would give us ...
select *
from
t1 cross join t2 cross join t3
;
ID LETTER ID LETTER ID LETTER
1 a 1 d 1 g
1 a 1 d 2 h
1 a 1 d 3 i
1 a 2 e 1 g
1 a 2 e 2 h
1 a 2 e 3 i
...
-- 27 rows
Related topics: CROSS APPLY (see documentation and examples here).

Oracle SQL Create Corresponding Labels for Paired Right Left Sequential Numbers with Identifiers

I am trying to pair left right parts in a table. I received an answer for a very similar question pairing left right parts but I now need to create a new column stating the corresponding paired part.
For example I have
ID LR Identifier
1 L B15A
2 R A15C
3 L A15C
4 R A15C
5 L A15C
6 R D5A2
9 R D5A2
10 L E5A6
11 R E5A6
12 L E5A6
13 R E5A6
14 R H9S5
17 L EE5A
18 R EE5A
and I need the query to return
ID LR Identifier Counterhand Counterpart
2 R A15C L 3
3 L A15C R 2
4 R A15C L 5
5 L A15C R 4
10 L E5A6 R 11
11 R E5A6 L 10
12 L E5A6 R 13
13 R E5A6 L 12
17 L EE5A R 18
18 R EE5A L 17
The link to the previous question is Here. Very helpfully answered by #mathguy
You can modify #mathguys previous solution and check if each result is the first or second in a pair:
mod(row_number() over (partition by identifier order by id), 2)
will give you either 0 or 1; and then you can then choose to either lead or lag to get the previous or next hand/ID value:
case mod(row_number() over (partition by identifier order by id), 2)
when 0 then lag(lr) over (partition by identifier order by id)
else lead(lr) over (partition by identifier order by id) end as counterhand
Blatantly copying and extending #mathguy's previous code:
with
test_data ( id, lr, identifier ) as (
select 001, 'L', 'B15A' from dual union all
select 002, 'R', 'A15C' from dual union all
select 003, 'L', 'A15C' from dual union all
select 004, 'R', 'A15C' from dual union all
select 005, 'L', 'A15C' from dual union all
select 006, 'R', 'D5A2' from dual union all
select 009, 'R', 'D5A2' from dual union all
select 010, 'L', 'E5A6' from dual union all
select 011, 'R', 'E5A6' from dual union all
select 012, 'L', 'E5A6' from dual union all
select 013, 'R', 'E5A6' from dual union all
select 014, 'R', 'H9S5' from dual union all
select 017, 'L', 'EE5A' from dual union all
select 018, 'R', 'EE5A' from dual
)
-- end of test data, the solution (SQL query) begins below this line
select id, lr, identifier,
case mod(row_number() over (partition by identifier order by id), 2)
when 0 then lag(lr) over (partition by identifier order by id)
else lead(lr) over (partition by identifier order by id) end as counterhand,
case mod(row_number() over (partition by identifier order by id), 2)
when 0 then lag(id) over (partition by identifier order by id)
else lead(id) over (partition by identifier order by id) end as counterpart
from ( select id, lr, identifier,
row_number() over (partition by identifier, lr order by id) as rn,
least( count(case when lr = 'L' then 1 end) over (partition by identifier),
count(case when lr = 'R' then 1 end) over (partition by identifier)
) as least_count
from test_data
)
where rn <= least_count
order by id -- ORDER BY is optional
;
gives you:
ID L IDEN C COUNTERPART
---------- - ---- - -----------
2 R A15C L 3
3 L A15C R 2
4 R A15C L 5
5 L A15C R 4
10 L E5A6 R 11
11 R E5A6 L 10
12 L E5A6 R 13
13 R E5A6 L 12
17 L EE5A R 18
18 R EE5A L 17
10 rows selected.
The same two case statements can be added to the second version in #mathguy's previous answer, and gives the same result.
Continuing to build on prior answer... #mathguy did the hard work, and after reading Alex's answer I think his is more compact, i'm using a union that he's not, we both used ROw_number and mod.
with cte2(ID, LR, Identifier) as (
SELECT 1,'L','B15A' FROM DUAL UNION ALL
SELECT 2,'R','A15C' FROM DUAL UNION ALL
SELECT 3,'L','A15C' FROM DUAL UNION ALL
SELECT 4,'R','A15C' FROM DUAL UNION ALL
SELECT 5,'L','A15C' FROM DUAL UNION ALL
SELECT 6,'R','D5A2' FROM DUAL UNION ALL
SELECT 9,'R','D5A2' FROM DUAL UNION ALL
SELECT 10,'L','E5A6' FROM DUAL UNION ALL
SELECT 11,'R','E5A6' FROM DUAL UNION ALL
SELECT 12,'L','E5A6' FROM DUAL UNION ALL
SELECT 13,'R','E5A6' FROM DUAL UNION ALL
SELECT 14,'R','H9S5' FROM DUAL UNION ALL
SELECT 17,'L','EE5A' FROM DUAL UNION ALL
SELECT 18,'R','EE5A' FROM DUAL),
cte1 as (select id, lr, identifier
from ( select id, lr, identifier,
row_number() over (partition by identifier, lr order by id) as rn,
least( count(case when lr = 'L' then 1 end) over (partition by identifier),
count(case when lr = 'R' then 1 end) over (partition by identifier)
) as least_count
from Cte2
)
where rn <= least_count
order by id),
cte as (Select A.*, Row_number() over (order by ID) RN from cte1 A)
SELECT A.Id
, A.LR
, A.Identifier
, case when A.LR = 'R' then 'L'
when A.LR = 'L' then 'R' end as CounterHand
, B.ID as CounterPart
FROM Cte A
INNER JOIN cte B
on A.RN+1 = B.RN
WHERE mod(A.RN,2)=1
UNION
SELECT B.Id
, B.LR
, B.Identifier
, case when B.LR = 'R' then 'L'
when B.LR = 'L' then 'R' end as CounterHand
, A.ID as CounterPart
FROM cte A
INNER JOIN cte B
on A.RN = B.RN-1
WHERE mod(B.RN,2)=0

Select Top (Max) Amount From Two Of Four Fields (Columns)

I have this query
SELECT code, username, week1money, week2money, week3money, week4money FROM(
--subquery goes here
)
How to select the top two weeks, i.e. weeks with the highest value? I want to sum the top two weeks to be precise.
If I understand correct you want to get 2 top values per every (code, username) row and (code, username) is a key of recordset.
Supposing you can have two top weeks with the same values and you don't have nulls this might be one of solutions:
SQL> with t (id, code, week1, week2, week3, week4)
2 as (
3 select 1, 'a', 10, 15, 11, 8 from dual union all
4 select 2, 'b', 7, 4, 2, 9 from dual union all
5 select 3, 'c', 3, 3, 1, 0 from dual
6 )
7 select id, code, max(week) first_top, min(week) next_top from (
8 select id, code, row_number() over(partition by id, code order by week desc) rnk, week
9 from (
10 select t.id, t.code,
11 decode(r.rn,1,week1,2,week2,3,week3,4,week4) week
12 from t,
13 (select rownum rn from dual connect by level <= 4) r
14 ))
15 where rnk in (1,2)
16 group by id, code
17 /
ID C FIRST_TOP NEXT_TOP
---------- - ---------- ----------
3 c 3 3
1 a 15 11
2 b 9 7
If you have non-null and different values in weeks you can use something like:
SQL> with t (id, code, week1, week2, week3, week4)
2 as (
3 select 1, 'a', 10, 15, 11, 8 from dual union all
4 select 2, 'b', 7, 4, 2, 9 from dual union all
5 select 3, 'c', 3, 2, 1, 0 from dual
6 )
7 select id, code
8 , greatest(week1, week2, week3, week4) first_top
9 , greatest(
10 case when week1 < greatest(week1, week2, week3, week4) then week1 else -1e28 end,
11 case when week2 < greatest(week1, week2, week3, week4) then week2 else -1e28 end,
12 case when week3 < greatest(week1, week2, week3, week4) then week3 else -1e28 end,
13 case when week4 < greatest(week1, week2, week3, week4) then week4 else -1e28 end
14 ) second_top
15 from t
16 /
ID C FIRST_TOP SECOND_TOP
---------- - ---------- ----------
1 a 15 11
2 b 9 7
3 c 3 2
But to get the right solution more details are required.
Answering my question...
select * from(
select * from(
select week1money col from dual
union
select week2money col from dual
union
select week3money col from dual
union
select week4money col from dual
) order by col desc
) where rownum < 3
Using GREATESTS() also may help.

Resources