Store bitmap to appropriate column - Oracle - sql

I have a table:
table1
id col val rec_pos
1 Test 1 10 1
1 Test 2 20 2
1 Test 3 30 3
1 Test 2 20 4
1 Empty 0101 5
1 Invalid 1011 6
2 Test 4 30 2
2 Test 5 30 3
2 Test 6 30 4
2 Test 5 30 5
2 Empty 11101 6
2 Invalid 10101 7
3 Test 7 30 5
3 Test 8 30 6
3 Test 8 30 7
3 Test 8 30 8
3 Empty 11110101 9
3 Invalid 10010101 10
4 Test 9 30 3
4 Empty 1101 5
4 Invalid 1011 6
For each unique id, I need to grab val for col = 'Empty' and then using rec_pos, add a new column called empty and add the appropriate values.
Same for invalid, for each unique id, I need to grab val for col = 'Invalid' and then using rec_pos, add a new column called invalid and add the appropriate values.
Example:
For id = 1, col = Empty, val = 0101
rec_pos = 1, add value in empty column = 0
rec_pos = 2, add value in empty column = 1
rec_pos = 3, add value in empty column = 0
rec_pos = 4, add value in empty column = 1
For id = 4, col = Empty, val = 1101
rec_pos = 1, add value in empty column = 1
rec_pos = 2, add value in empty column = 1
rec_pos = 3, add value in empty column = 0
rec_pos = 4, add value in empty column = 1
Output would be:
id col val rec_pos empty invalid
1 Test 1 10 1 0 1
1 Test 2 20 2 1 0
1 Test 3 30 3 0 1
1 Test 2 20 4 1 1
1 Empty 0101 5 0 0
1 Invalid 1011 6 0 0
2 Test 4 30 2 1 0
2 Test 5 30 3 1 1
2 Test 6 30 4 0 0
2 Test 5 30 5 1 1
2 Empty 11101 6 0 0
2 Invalid 10101 7 0 0
3 Test 7 30 5 0 0
3 Test 8 30 6 1 1
3 Test 8 30 7 0 0
3 Test 8 30 8 1 1
3 Empty 11110101 9 0 0
3 Invalid 10010101 10 0 0
4 Test 9 30 3 0 1
4 Empty 1101 5 0 0
4 Invalid 1011 6 0 0
How can I read the values and then appropriately assign it to respective rec_pos for the same id?

If position is determined by rec_pos then you can use simply susbtr combined with analytical max:
select t.*,
nvl(substr(max(case col when 'Empty' then val end)
over (partition by id), rec_pos, 1), 0) empty,
nvl(substr(max(case col when 'Invalid' then val end)
over (partition by id), rec_pos, 1), 0) invalid
from table1 t
or with correlated subqueries:
select t.*,
nvl(substr((select val
from table1 e
where e.id = t.id and col= 'Empty'), rec_pos, 1), 0) empty,
nvl(substr((select val
from table1 i
where i.id = t.id and col= 'Invalid'), rec_pos, 1), 0) invalid
from table1 t
dbfiddle demo
If empty and invald are real columns in your table then use simple merge with any of the above queries to update them. But it's better to make view when a column results from calculations from others.

Test case (only for IDs 1 and 4; didn't feel like typing that much):
SQL> create table test
2 (id number,
3 col varchar2(10),
4 val varchar2(10),
5 rec_pos number,
6 empty number,
7 invalid number);
Table created.
SQL> insert into test (id, col, val, rec_pos)
2 (select 1, 'test 1', '10' , 1 from dual union all
3 select 1, 'test 2' , '20' , 2 from dual union all
4 select 1, 'test 3' , '30' , 3 from dual union all
5 select 1, 'test 2' , '20' , 4 from dual union all
6 select 1, 'empty' , '0101', 5 from dual union all
7 select 1, 'invalid', '1011', 6 from dual union all
8 --
9 select 4, 'test 9' , '30' , 3 from dual union all
10 select 4, 'empty' , '1101', 5 from dual union all
11 select 4, 'invalid', '1011', 6 from dual
12 );
9 rows created.
SQL> select * From test;
ID COL VAL REC_POS EMPTY INVALID
---------- ---------- ---------- ---------- ---------- ----------
1 test 1 10 1
1 test 2 20 2
1 test 3 30 3
1 test 2 20 4
1 empty 0101 5
1 invalid 1011 6
4 test 9 30 3
4 empty 1101 5
4 invalid 1011 6
9 rows selected.
SQL>
Update statement:
SQL> update test e set
2 e.empty = (select nvl(x.digit, 0)
3 from ( select t.id,
4 substr(t.val, column_value, 1) digit,
5 column_value rec_pos
6 from test t join
7 table(cast(multiset(select level from dual
8 connect by level <= (select max(t1.rec_pos)
9 from test t1
10 where t1.id = t.id
11 )
12 ) as sys.odcinumberlist ))
13 on 1 = 1
14 where t.col = 'empty'
15 ) x
16 where x.id = e.id
17 and x.rec_pos = e.rec_pos
18 ),
19 --
20 e.invalid = (select nvl(x.digit, 0)
21 from ( select t.id,
22 substr(t.val, column_value, 1) digit,
23 column_value rec_pos
24 from test t join
25 table(cast(multiset(select level from dual
26 connect by level <= (select max(t1.rec_pos)
27 from test t1
28 where t1.id = t.id
29 )
30 ) as sys.odcinumberlist ))
31 on 1 = 1
32 where t.col = 'invalid'
33 ) x
34 where x.id = e.id
35 and x.rec_pos = e.rec_pos
36 );
9 rows updated.
SQL>
What does it do?
takes VAL and splits it into rows
position (for SUBSTR) is determined by the maximum REC_POS for that ID, so that you could use NVL later on, while updating EMPTY (or INVALID) column
basically, both updates are equal, they differ only in what you're updating (lines #14 and #32)
Result:
SQL> select * from test;
ID COL VAL REC_POS EMPTY INVALID
---------- ---------- ---------- ---------- ---------- ----------
1 test 1 10 1 0 1
1 test 2 20 2 1 0
1 test 3 30 3 0 1
1 test 2 20 4 1 1
1 empty 0101 5 0 0
1 invalid 1011 6 0 0
4 test 9 30 3 0 1
4 empty 1101 5 0 0
4 invalid 1011 6 0 0
9 rows selected.
SQL>

I think that this gives you the output that you asked for.
SELECT t.id,
t.col,
t.val,
t.rec_pos,
NVL(e.empty, 0) AS empty,
NVL(i.invalid, 0) AS invalid
FROM
table1 t
LEFT
JOIN (SELECT DISTINCT t.id, levels.column_value AS rec_pos, SUBSTR(val, levels.column_value, 1) AS empty
FROM table1 t, TABLE(CAST(MULTISET(SELECT level FROM dual CONNECT BY level <= LENGTH(val)) AS sys.OdciNumberList)) levels
WHERE t.col = 'Empty' ORDER BY id) e ON e.id = t.id AND e.rec_pos = t.rec_pos
LEFT
JOIN (SELECT DISTINCT t.id, levels.column_value AS rec_pos, SUBSTR(val, levels.column_value, 1) AS invalid
FROM table1 t, TABLE(CAST(MULTISET(SELECT level FROM dual CONNECT BY level <= LENGTH(val)) AS sys.OdciNumberList)) levels
WHERE t.col = 'Invalid' ORDER BY id) i ON i.id = t.id AND i.rec_pos = t.rec_pos
ORDER
BY t.id, t.rec_pos;

Related

Oracle add sequential query rows if missing

In Oracle 19c I have a data like:
with t as (
select 1 Cat, 1 id, 11 val from dual
union all
select 1, 3, 33 from dual
union all
select 2, 2, 22 from dual
union all
select 2, 4, 44 from dual)
select *
from t
In query result I want to get 4 rows per every cat with ids 1-4 and if there was no such id in that cat a val must be null:
cat
id
val
1
1
11
1
2
1
3
33
1
4
2
1
2
2
22
2
3
2
4
44
Use a PARTITIONed join with a row-generator:
SELECT t.cat,
i.id,
t.val
FROM (SELECT LEVEL AS id FROM DUAL CONNECT BY LEVEL <= 4) i
LEFT OUTER JOIN t
PARTITION BY (t.cat)
ON (i.id = t.id)
Which outputs:
CAT
ID
VAL
1
1
11
1
2
null
1
3
33
1
4
null
2
1
null
2
2
22
2
3
null
2
4
44
db<>fiddle here

how to convert distinct row values into column [duplicate]

This question already has answers here:
Dynamic Pivot in Oracle's SQL
(10 answers)
Closed last year.
Here is my data
Id type count
1 jim 2
1 bim 2
1 sim 3
1 pim 1
2 jim 2
2 bim 1
Want to convert this data into below
Id jim bim sim pim
1 2 2 3 1
2 2 1 0 0
Tried this, its now working
select * FROM table
PIVOT
(
Min(Id)
FOR Id IN (select distinct(type) from table)
)
I'm trying to convert all distinct values of type row into columns and then assign respective values against every ID. Any suggestion please ?
Error
ORA-00936: missing expression
00936. 00000 - "missing expression"
*Cause:
*Action:
Error at Line: 26 Column: 16
Would this do?
SQL> with test (id, type, count) as
2 (select 1, 'jim', 2 from dual union all
3 select 1, 'bim', 2 from dual union all
4 select 1, 'sim', 3 from dual union all
5 select 1, 'pim', 1 from dual union all
6 select 2, 'jim', 2 from dual union all
7 select 2, 'bim', 1 from dual
8 )
9 select id, nvl(jim, 0) jim,
10 nvl(bim, 0) bim,
11 nvl(sim, 0) sim,
12 nvl(pim, 0) pim
13 from test
14 pivot
15 (min(count)
16 for type in ('jim' as jim, 'bim' as bim, 'sim' as sim, 'pim' as pim)
17 );
ID JIM BIM SIM PIM
---------- ---------- ---------- ---------- ----------
1 2 2 3 1
2 2 1 0 0
SQL>

Filtering SQL using a oracle database

I would like to know if the following is possible
For example I have a shoe factory. In this factory I have a production line, Every step in this production line is recorded into the oracle database.
if the shoe has completed a production step the result is = 1
example table
Shoe_nr production step result
1 1 1
1 2 1
1 3
2 1 1
2 2 1
2 3
3 1
3 2
3 3
Now the question, is it possible to filter out production step 3 where only the shoes have passed production step 2 which is equal to 1 in result.
I know if it can be done it's probably very easy but if you dont know i found out it's a little bit tricky.
Thanks,
Chris
Yes, you can do it with IN and a Subselect
select *
from shoes
where shoe.id in (
select shoe.id
from shoes
where production_step = 2
and result = 1
)
and production_step = 3
This might be one option; see comments within code (lines #1 - 12 represent sample data; you already have that and don't type it. Query you might be interested in begins at line #13).
SQL> with shoes (shoe_nr, production_step, result) as
2 -- sample data
3 (select 1, 1, 1 from dual union all
4 select 1, 2, 1 from dual union all
5 select 1, 3, null from dual union all
6 select 2, 1, 1 from dual union all
7 select 2, 2, 1 from dual union all
8 select 2, 3, null from dual union all
9 select 3, 1, null from dual union all
10 select 3, 2, null from dual union all
11 select 3, 3, null from dual
12 ),
13 -- which shoes' production step #3 should be skipped?
14 skip as
15 (select shoe_nr
16 from shoes
17 where production_step = 2
18 and result = 1
19 )
20 -- finally:
21 select a.shoe_nr, a.production_step, a.result
22 from shoes a
23 where (a.shoe_nr, a.production_step) not in (select b.shoe_nr, 3
24 from skip b
25 )
26 order by a.shoe_nr, a.production_step;
SHOE_NR PRODUCTION_STEP RESULT
---------- --------------- ----------
1 1 1
1 2 1
2 1 1
2 2 1
3 1
3 2
3 3
7 rows selected.
SQL>
If you just want the shoe_nr that satisfy the condition, you can use aggregation and a having clause:
select shoe_nr
from mytable
group by shoe_nr
having
max(case when production_step = 2 then result end) = 0
and max(case when production_step = 3 then 1 end) = 1
If you want the entire row corresponding to this shoe_nr at step 3, use window functions instead:
select 1
from (
select
t.*,
max(case when production_step = 2 then result end)
over(partition by shoe_nr) as has_completed_step_2
from mytable t
) t
where production_step = 3 and has_completed_step_2 = 0

where condition scenario in oracle

i have requirement in where condition
if my id is in 1 then it should check id 4,5,6,7 or it should check value which is in id
this id i will pass as parameter to query
select * from table_a where id
Help me in this
You can use the below
select * from individual ip
where (
( :p_prs_nat = 219 and ip.prs_nationality_id in (231,259,343) )
or (:p_prs_nat <> 219 and :p_prs_nat=ip.prs_nationality_id
))
where ip.prs_nationality_id =case when :p_prs_nat in( 219) then it shud check (231,259,343) else :p_prs_nat end how to achieve this functionality
You cannot directly use IN while returning the result in the THEN clause of CASE expression. However, you could first check the condition itself using AND operator and return TRUE whenever it matches.
For example,
SQL> WITH DATA AS
2 (
3 SELECT 1 ID, 'X' STR FROM DUAL UNION ALL
4 SELECT 2 ID, 'A' STR FROM DUAL UNION ALL
5 SELECT 3 ID ,'P' STR FROM DUAL UNION ALL
6 SELECT 4 ID ,'Q' STR FROM DUAL
7 )
8 SELECT *
9 FROM DATA
10 WHERE (
11 CASE
12 WHEN ID = 1
13 AND str IN ('A','Y','Z')
14 THEN 1
15 WHEN ID <> 1
16 THEN 1
17 END ) =1
18 /
ID S
---------- -
2 A
3 P
4 Q
SQL>
So, you did not get the row with ID = 1,since it did not match the condition AND str IN ('A','Y','Z').
If it would match, it will return those matching rows too:
SQL> WITH DATA AS
2 (
3 SELECT 1 ID, 'X' STR FROM DUAL UNION ALL
4 SELECT 2 ID, 'A' STR FROM DUAL UNION ALL
5 SELECT 3 ID ,'P' STR FROM DUAL UNION ALL
6 SELECT 4 ID ,'Q' STR FROM DUAL
7 )
8 SELECT *
9 FROM DATA
10 WHERE (
11 CASE
12 WHEN ID = 1
13 AND str IN ('X','Y','Z')
14 THEN 1
15 WHEN ID <> 1
16 THEN 1
17 END ) =1
18 /
ID S
---------- -
1 X
2 A
3 P
4 Q
SQL>

SQL pad query result for missing groups

Assume the following table:
TableA:
ID GroupName SomeValue
1 C 1
2 C 1
2 B 1
2 A 1
I need to construct a query that selects the following result:
ID GroupName SomeValue
1 C 1
1 B 0
1 A 0
2 C 1
2 B 1
2 A 1
The GroupName is actually derived from TableA column's CASE expression and can take only 3 values: A, B, C.
Are the analytic functions the way to go?
EDIT
Sorry, for not mentioning it, but the ID could consist of multiple columns. Consider this example:
ID1 ID2 GroupName SomeValue
1 1 C 1
1 2 C 1
2 2 C 1
2 2 B 1
2 2 A 1
I need to pad SomeValue with 0 for each unique combination ID1+ID2. So the result should be like this:
ID1 ID2 GroupName SomeValue
1 1 C 1
1 1 B 0
1 1 A 0
1 2 C 1
1 2 B 0
1 2 A 0
2 2 C 1
2 2 B 1
2 2 A 1
EDIT2
Seems like solution, proposed by #Laurence should work even for multiple-column 'ID'. I couldn't rewrite the query proposed by #Nicholas Krasnov to conform to this requirement. But could somebody compare these solutions performance-wise? Will the analytic function work faster than 'cross join + left outer join'?
To fill in gaps, you could write a similar query using partition by clause of outer join:
SQL> with t1(ID,GroupName,SomeValue) as
2 (
3 select 1, 'C', 1 from dual union all
4 select 2, 'C', 1 from dual union all
5 select 2, 'B', 1 from dual union all
6 select 2, 'A', 1 from dual
7 ),
8 groups(group_name) as(
9 select 'A' from dual union all
10 select 'B' from dual union all
11 select 'C' from dual
12 )
13 select t1.ID
14 , g.group_name
15 , nvl(SomeValue, 0) SomeValue
16 from t1
17 partition by (t1.Id)
18 right outer join groups g
19 on (t1.GroupName = g.group_name)
20 order by t1.ID asc, g.group_name desc
21 ;
ID GROUP_NAME SOMEVALUE
---------- ---------- ----------
1 C 1
1 B 0
1 A 0
2 C 1
2 B 1
2 A 1
6 rows selected
UPDATE: Response to the comment.
Specify ID2 column in the partition by clause as well:
SQL> with t1(ID1, ID2, GroupName,SomeValue) as
2 (
3 select 1, 1, 'C', 1 from dual union all
4 select 1, 2, 'C', 1 from dual union all
5 select 2, 2, 'C', 1 from dual union all
6 select 2, 2, 'B', 1 from dual union all
7 select 2, 2, 'A', 1 from dual
8 ),
9 groups(group_name) as(
10 select 'A' from dual union all
11 select 'B' from dual union all
12 select 'C' from dual
13 )
14 select t1.ID1
15 , t1.ID2
16 , g.group_name
17 , nvl(SomeValue, 0) SomeValue
18 from t1
19 partition by (t1.Id1, t1.Id2)
20 right outer join groups g
21 on (t1.GroupName = g.group_name)
22 order by t1.ID1, t1.ID2 asc , g.group_name desc
23 ;
ID1 ID2 GROUP_NAME SOMEVALUE
---------- ---------- ---------- ----------
1 1 C 1
1 1 B 0
1 1 A 0
1 2 C 1
1 2 B 0
1 2 A 0
2 2 C 1
2 2 B 1
2 2 A 1
9 rows selected
Select
i.Id1,
i.Id2,
g.GroupName,
Coalesce(a.SomeValue, 0) As SomeValue
From
(select distinct ID1, ID2 from TableA) as i
cross join
(select distinct GroupName from TableA) as g
left outer join
tableA a
on i.ID = a.ID and g.GroupName = a.GroupName
Order By
1,
2,
3 Desc