Oracle SQL - Create identifier for couple of values - sql

I'm struggling with the following problem, I have the follwing data in a table:
Param ID
Param Val
Other Cols
1
15
XXX
1
15
XXX
1
16
XXX
1
16
XXX
2
21
XXX
2
21
XXX
2
22
XXX
2
22
XXX
I would like to select a new colum in order to create 4 sets of data to have all the possible combination between the values of parameter 1 and 2; so I would like to obtain something like this:
Set
Param ID
Param Val
Other Cols
1
1
15
XXX
2
1
15
XXX
3
1
16
XXX
4
1
16
XXX
1
2
21
XXX
3
2
21
XXX
2
2
22
XXX
4
2
22
XXX
So for example for the Set 1 I will have the Couple of values 15 and 21, for the set 2 the values 15 and 22 etc etc.
I tried using different analytic functions, but I was not able to have what I need.
Thanks in advance.

Despite a good hint on the MODEL clause, I guess I will go for a solution combining cross join and unpivot, maybe it is not the best, but it fit my needs.
WITH tbl AS
(
Select 1 "ID", 15 "VAL" From Dual Union All
Select 1 "ID", 16 "VAL" From Dual Union All
Select 2 "ID", 21 "VAL" From Dual Union All
Select 2 "ID", 22 "VAL" From Dual )
SELECT *
FROM (
SELECT ROWNUM AS SET_ID,
id1,
id4
FROM (
SELECT
CASE a.id
WHEN 1 THEN a.val
ELSE 0
END AS id1,
CASE a.id
WHEN 2 THEN a.val
ELSE 0
END AS id2,
CASE b.id
WHEN 1 THEN b.val
ELSE 0
END AS id3,
CASE b.id
WHEN 2 THEN b.val
ELSE 0
END AS id4
FROM tbl a,
tbl b)
WHERE id2 = 0
AND id3 = 0) UNPIVOT (VAL FOR ID IN (id1 AS '1',
id4 AS '2'))
that results in :
SET_ID
ID
VAL
1
1
15
1
2
21
2
1
15
2
2
22
3
1
16
3
2
21
4
1
16
4
2
22
tried with all the combination of source data, and it seems work :)

Sample data:
WITH
tbl AS
(
Select 1 "ID", 15 "VAL" From Dual Union All
Select 1 "ID", 15 "VAL" From Dual Union All
Select 1 "ID", 16 "VAL" From Dual Union All
Select 1 "ID", 16 "VAL" From Dual Union All
Select 2 "ID", 21 "VAL" From Dual Union All
Select 2 "ID", 21 "VAL" From Dual Union All
Select 2 "ID", 22 "VAL" From Dual Union All
Select 2 "ID", 22 "VAL" From Dual
)
UPDATED AFTER THE COMMENT
If you want the combinations then you will have to use the MODEL clause. To do that you should prepare the data (cte named grid) a bit so you could do the addressing to the particular data and manage all the combinations you want. It looks like here:
grid AS
( Select Distinct
Sum(1) OVER(Partition By ID Order By ID ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) "IDS_TOTAL_ORDER",
Sum(1) OVER(Partition By VAL Order By VAL ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) "VALS_TOTAL_ORDER",
ID "ID",
VAL "VAL"
From
tbl
Order By ID
)
Select SET_ID, ID, VAL
From ( Select 0 "SET_ID", IDS_TOTAL_ORDER, VALS_TOTAL_ORDER, ID "ID", VAL "VAL"
From grid
)
MODEL
Dimension By (ID, IDS_TOTAL_ORDER, VALS_TOTAL_ORDER)
Measures(SET_ID, VAL)
RULES
(
SET_ID[1, ANY, ANY] = CV(IDS_TOTAL_ORDER),
SET_ID[2, 1, 1] = CV(IDS_TOTAL_ORDER),
SET_ID[2, 2, 2] = CV(IDS_TOTAL_ORDER) + 1,
SET_ID[2, 3, 1] = CV(IDS_TOTAL_ORDER) - 1,
SET_ID[2, 4, 2] = CV(IDS_TOTAL_ORDER)
)
Order By ID, IDS_TOTAL_ORDER
This way you can get any combination. Here is your result:
SET_ID
ID
VAL
1
1
15
2
1
15
3
1
16
4
1
16
1
2
21
3
2
21
2
2
22
4
2
22
More about MODEL clause: https://www.oracle.com/webfolder/technetwork/tutorials/obe/db/10g/r2/prod/bidw/sqlmodel/sqlmodel_otn.htm
Sets now looks like here:
SET_ID
IDS_VALS
1
1/15, 2/21
2
1/15, 2/22
3
1/16, 2/21
4
1/16, 2/22
Regards...

Related

Count occurences of values in table, when I treat one value as occurence of all other values

I have a table with one column (just to simplify the problem) with values 0-23 or *.
I want to count occurrences of each value 0-23, but treat * as occurrence of all other values
for example:
column_name
-------------
3
4
5
6
7
*
4
4
3
*
I want to get something like that:
column_name | count
--------------------
1 | 2
2 | 2
3 | 4
4 | 5
5 | 3
6 | 3
7 | 3
.....
I tried experimenting with different count and "group by" methods, but always getting very strange results. Basically the main problem here is to how count rows when I need to have one value in all other groups.
You could use analytic function that counts values where * is replaced by actual value between 0 and 23:
SELECT DISTINCT n.RN "COL_1", Count(REPLACE(t.COL_1, '*', n.RN)) OVER(Partition By n.RN) "CNT"
FROM tbl t
INNER JOIN ( Select To_Char(LEVEL - 1) "RN" From Dual Connect By LEVEL <=24 ) n ON(n.RN = REPLACE(t.COL_1, '*', n.RN))
WHERE n.RN IN(SELECT COL_1 FROM tbl)
ORDER BY To_Number(n.RN)
which with your sample data:
WITH
tbl (COL_1) AS
(
Select '3' From Dual Union All
Select '4' From Dual Union All
Select '5' From Dual Union All
Select '6' From Dual Union All
Select '7' From Dual Union All
Select '*' From Dual Union All
Select '4' From Dual Union All
Select '4' From Dual Union All
Select '3' From Dual Union All
Select '*' From Dual Union All
Select '3' From Dual
)
... results as:
COL_1 CNT
---------------------------------------- ----------
3 5
4 5
5 3
6 3
7 3
... and if you exclude the Where clause you will get all the rows (0 - 23) with number of occurances counted by REPLACE of * with any of the numbers
COL_1 CNT
---------------------------------------- ----------
0 2
1 2
2 2
3 5
4 5
5 3
6 3
7 3
8 2
9 2
10 2
11 2
12 2
13 2
14 2
15 2
16 2
17 2
18 2
19 2
20 2
21 2
22 2
23 2
You can do it using successive WITH's :
First one to calculate number of occurrence of *.
And the second is to calculate number of occurrence of each number.
with cte as (
select count(1) as c
from mytable
where column_name = '*'
),
cte2 as (
select column_name, count(1) as c
from mytable, cte
group by column_name
)
select column_name, cte.c + cte2.c
from cte2, cte;
You can with nested statements too,
SELECT ID,(count_ + (
SELECT COUNT(ID) FROM sql_test_a
WHERE ID = '*')) as count_
FROM (
SELECT ID,COUNT(ID) as count_
FROM sql_test_a WHERE ID != '*' GROUP BY ID);

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

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