How to join table with result column in oracle - sql

I used case when to get a column named 'code' in oracle. Now I want to get another column from a table which need to be joined with the result 'code' column.
Example:
select
(case when field='A' then '001'
when field='B' then '002'
end) code
from table_code
Now I want to get another column 'code_name' in this query from table 'names' containing data as
code: 001= code_name: aa & code: 002= code_name: bb

WITH clause is here just to generate some sample data, and as such, it is not a part of the answer.
Just guessing what you are asking for, but if I got it right the answer is below.
First the sample data:
WITH
ids_data AS
(
Select 'A' "CODE_ID", 'ABCD_123' "SOME_FLD", 10 "AMOUNT" From Dual Union All
Select 'B' "CODE_ID", 'BCD_123' "SOME_FLD", 25 "AMOUNT" From Dual Union All
Select 'C' "CODE_ID", 'CD_123' "SOME_FLD", 10 "AMOUNT" From Dual Union All
Select 'D' "CODE_ID", 'D_123' "SOME_FLD", 30 "AMOUNT" From Dual Union All
Select 'E' "CODE_ID", 'X123' "SOME_FLD", 15 "AMOUNT" From Dual U
),
names_data AS
(
Select '001' "CODE_ID", 'Code Name 001' "CODE_NAME" From Dual Union All
Select '002' "CODE_ID", 'Code Name 002' "CODE_NAME" From Dual Union All
Select '003' "CODE_ID", 'Code Name 003' "CODE_NAME" From Dual Union All
Select '004' "CODE_ID", 'Code Name 004' "CODE_NAME" From Dual Union All
Select '005' "CODE_ID", 'Code Name 005' "CODE_NAME" From Dual U
)
The answer is - just use the CASE statement in the join condition ON clause like you did it in the select clause.
SELECT
i.CODE_ID "CODE_ID",
CASE
WHEN i.CODE_ID = 'A' THEN '001'
WHEN i.CODE_ID = 'B' THEN '002'
WHEN i.CODE_ID = 'C' THEN '003'
WHEN i.CODE_ID = 'D' THEN '004'
WHEN i.CODE_ID = 'E' THEN '005'
ELSE
'000'
END "CODE_ID_2",
n.CODE_NAME "CODE_NAME",
i.SOME_FLD "SOME_FFLD",
i.AMOUNT "AMOUNT"
FROM
ids_data i
INNER JOIN
names_data n
ON(n.CODE_ID = CASE
WHEN i.CODE_ID = 'A' THEN '001'
WHEN i.CODE_ID = 'B' THEN '002'
WHEN i.CODE_ID = 'C' THEN '003'
WHEN i.CODE_ID = 'D' THEN '004'
WHEN i.CODE_ID = 'E' THEN '005'
ELSE
'000'
END)
Here is the result
--
-- R e s u l t :
--
-- CODE_ID CODE_ID_2 CODE_NAME SOME_FFLD AMOUNT
-- ------- --------- ------------- --------- ----------
-- A 001 Code Name 001 ABCD_123 10
-- B 002 Code Name 002 BCD_123 25
-- C 003 Code Name 003 CD_123 10
-- D 004 Code Name 004 D_123 30
-- E 005 Code Name 005 X123 15

Related

SQL query to find products which have different values for a particular id which should be the same

I need a SQL query which will fetch me the list of products which has different values for a same id and product is of table A and Id and values are of table B and both the tables can be joined by column name prod_id
Output I want:
List item
product
abc
So in output I want only the product abc because it has different values for their respective id and I don’t need xyz because it has same values for their respective id
I tried but I’m not getting what I want as mentioned above
select distinct product
from your_table
group by product, id
having count(distinct values) > 1
If I got it right, you have two tables - one with PROD_ID and PRODUCT and the other with PROD_ID, ID, VALS - something like this:
WITH
tbl_a AS
(
Select 1 "PROD_ID", 'abc' "PRODUCT" From DUAL Union All
Select 9 "PROD_ID", 'xyz' "PRODUCT" From DUAL
),
tbl_b AS
(
Select 1 "PROD_ID", '10' "ID", 345678 "VALS" From DUAL Union All
Select 1 "PROD_ID", '10' "ID", 345678 "VALS" From DUAL Union All
Select 1 "PROD_ID", '14' "ID", 458292 "VALS" From DUAL Union All
Select 1 "PROD_ID", '13' "ID", 629351 "VALS" From DUAL Union All
Select 1 "PROD_ID", '13' "ID", 629354 "VALS" From DUAL Union All
Select 9 "PROD_ID", '10' "ID", 375281 "VALS" From DUAL Union All
Select 9 "PROD_ID", '10' "ID", 375281 "VALS" From DUAL Union All
Select 9 "PROD_ID", '12' "ID", 826292 "VALS" From DUAL Union All
Select 9 "PROD_ID", '12' "ID", 826292 "VALS" From DUAL
)
Now, if this is the case, all you have to do is to join the tables by PROD_ID and select the product having more than one distinct value of VALS column grouped by PRODUCT and ID:
Select
a.PRODUCT
From
tbl_a a
Inner Join
tbl_b b ON(b.PROD_ID = a.PROD_ID)
Group By
a.PRODUCT, b.ID
Having
Count(DISTINCT b.VALS) > 1
--
-- Result:
-- PRODUCT
-- -------
-- abc

SQL, case statement not working. The error states that the field POS_Nbr is not aggregated or grouped. Any advice?

This is the sql code, the error is under the format line.
SELECT
Store,
FORMAT("%.0f%%",
CASE WHEN POS_Nbr in(60,61,62,63)
THEN (COUNT(DISTINCT POS_Unique_ID)/COUNT (Cal_Day))
ELSE 0 END) as percent_pure_ACO_mode_at_front_end,
FROM `ca-pr-cda-views.SALES.POS_SALES_ALL`
GROUP BY Store
ORDER BY Store;
The problem is within your CASE expresssion - in the below code your construct is commented out (as it errors) and there is a different construct of aggregation with CASE expression...
WITH
tbl AS
(
Select 1 "STORE_ID", 60 "POS_NBR", 'B' "POS_ID", SYSDATE-1 "CAL_DAY" From Dual Union All
Select 1 "STORE_ID", 10 "POS_NBR", 'B' "POS_ID", SYSDATE-2 "CAL_DAY" From Dual Union All
Select 1 "STORE_ID", 20 "POS_NBR", 'B' "POS_ID", SYSDATE-3 "CAL_DAY" From Dual Union All
Select 2 "STORE_ID", 61 "POS_NBR", 'A' "POS_ID", SYSDATE "CAL_DAY" From Dual Union All
Select 2 "STORE_ID", 62 "POS_NBR", 'A' "POS_ID", SYSDATE-1 "CAL_DAY" From Dual Union All
Select 1 "STORE_ID", 63 "POS_NBR", 'A' "POS_ID", SYSDATE-2 "CAL_DAY" From Dual Union All
Select 1 "STORE_ID", 60 "POS_NBR", 'B' "POS_ID", SYSDATE-3 "CAL_DAY" From Dual
)
SELECT
STORE_ID "STORE_ID",
-- CASE WHEN POS_Nbr in(60,61,62,63) THEN (COUNT(DISTINCT POS_Unique_ID)/COUNT (Cal_Day)) ELSE 0 END "PCT"
Count(DISTINCT CASE WHEN POS_NBR Between 60 And 63 THEN POS_ID ELSE '0' END) / Count(CAL_DAY) "PCT"
FROM
tbl
GROUP BY STORE_ID
ORDER BY STORE_ID
/* R e s u l t
STORE_ID PCT
---------- ----------
1 .6
2 .5
*/
You should adjust it to your needs and datatypes buut the problem with group by will dissappear if you aggregate values returned by CASE expresion instead of trying to select CASE expression of aggregated values. Regards...

Generate Result based on max count in secondary column after a join

I have two tables which have a common key between them, and quite a lot of other important infos ; for the sake of simplicity i will be using Combination A and Combination B. When a combination is met, whichever table has the maximum number of records should be the source where i collect the information ; in this case say IDs. The priority when counts are same is Table1.
COMMONKEY column is the combination/join condition in my tables.
(Table 1)
SELECT '123' table1_id,'Comb A' commonkey from dual UNION
SELECT '124' table1_id,'Comb A' commonkey from dual UNION
SELECT '125' table1_id,'Comb A' commonkey from dual UNION
SELECT '126' table1_id,'Comb A' commonkey from dual UNION
SELECT '215' table1_id,'Comb B' commonkey from dual UNION
SELECT '216' table1_id,'Comb B' commonkey from dual UNION
SELECT '559' table1_id,'Random Combination 1' commonkey from dual UNION
SELECT '560' table1_id,'Random Combination 2' commonkey from dual ;
( Table 2 )
SELECT 'abc1' table2_id,'Comb A' commonkey from dual UNION
SELECT 'abc2' table2_id,'Comb A' commonkey from dual UNION
SELECT 'abc3' table2_id,'Comb A' commonkey from dual UNION
SELECT 'abc4' table2_id,'Comb A' commonkey from dual UNION
SELECT 'xyz1' table2_id,'Comb B' commonkey from dual UNION
SELECT 'xyz2' table2_id,'Comb B' commonkey from dual UNION
SELECT 'xyz3' table2_id,'Comb B' commonkey from dual UNION
SELECT 'xyz2' table2_id,'Comb B' commonkey from dual UNION
SELECT '416abc1' table2_id,'Random Combination 91' commonkey from dual UNION
SELECT '416abc2' table2_id,'Random Combination 92' commonkey from dual;
Result Set Expected :
ID COMMONKEY
123 Comb A
124 Comb A
125 Comb A
126 Comb A
xyz1 Comb B
xyz2 Comb B
xyz3 Comb B
559 Random Combination 1
560 Random Combination 1
416abc1 Random Combination 91
416abc2 Random Combination 92
Updated Image :
( the image shows a screenshot of the trail data in an excel; The Requirement and Strategy are color mapped to make it quickly understandable )
I need to generate the result set using SQL as follows :
When table1.commonkey = table2.commonkey hits, I need to-
If table1 has 10 IDs, table2 has 5 IDs -> Pick 10 IDs from table1.
If table1 has 15 IDs, table2 has 30 IDs -> Pick 30 IDs from table2.
If table1 has 4 IDs, table2 has 4 IDs -> Pick 4 IDs from table1.
( when equal, choose table1 IDs )
When no matches occur with the common key, prevent a cross join and add in the rowsets linearly to the result table.
Edit : I've initially gone on routes with
a left join b where b.key IS null ;
a full outer join b where b.key IS NULL or a.key is NULL ;
to achieve workarounds with A-B or B-A result sets but both these approaches were quite wrong. Gathering Delta sets or Exclusion sets didnt go well.
Here's one option; see comments within code
SQL> with
2 -- sample data
3 a (id, ckey) as
4 (select '123', 'ca' from dual union all
5 select '124', 'ca' from dual union all
6 select '125', 'ca' from dual union all
7 select '126', 'ca' from dual union all
8 select '215', 'cb' from dual union all
9 select '216', 'cb' from dual union all
10 select '551', 'r1' from dual union all
11 select '552', 'r2' from dual
12 ),
13 b (id, ckey) as
14 (select 'abc1', 'ca' from dual union all
15 select 'abc2', 'ca' from dual union all
16 select 'abc3', 'ca' from dual union all
17 select 'abc4', 'ca' from dual union all
18 select 'xyz1', 'cb' from dual union all
19 select 'xyz2', 'cb' from dual union all
20 select 'xyz3', 'cb' from dual union all
21 select '9991', 'r3' from dual union all
22 select '9992', 'r4' from dual
23 ),
24 -- count rows per each CKEY (common key)
25 tempa as
26 (select id, ckey, count(*) over (partition by ckey) cnt
27 from a
28 ),
29 tempb as
30 (select id, ckey, count(*) over (partition by ckey) cnt
31 from b
32 )
33 -- final query
34 select distinct
35 case when a.cnt >= b.cnt then a.id
36 else b.id
37 end id,
38 a.ckey
39 from tempa a join tempb b on b.ckey = a.ckey
40 union all
41 select ckey, id from a
42 where not exists (select null from b where a.ckey = b.ckey)
43 union all
44 select ckey, id from b
45 where not exists (select null from a where a.ckey = b.ckey)
46 order by 1, 2;
which results in
ID CKEY
---- -----
r1 551
r2 552
r3 9991
r4 9992
xyz1 cb
xyz2 cb
xyz3 cb
123 ca
124 ca
125 ca
126 ca
11 rows selected.
SQL>

Oracle pivot - one table field split into rows and columns

I have an Oracle 12c table that looks something like this:
Name Class Type
--------------------------
Alice Math S
Alice Female A
Bob Anthropology S
Bob Male A
Charlie Science S
Charlie Tennis A
Charlie Male A
Do Math S
Do Female A
Do Tennis A
Elmer Male A
Elmer Science S
I would like to pivot the table to produce a report that looks like below. Classes of type "S" are rows, and type "A" are columns. And then count the number of people that fall into each category. For example, there are two people that are both Female and take class Math - Alice and Do.
Female Male Tennis Total
-----------------------------------------------
Math 2 0 1 3
Anthropology 0 1 0 1
Science 0 2 1 3
Total 2 3 2 7
I am familiar with pivot and cube in Oracle but I am stuck with getting the Class field split between rows and columns in the report.
I would really appreciate your help.
You can use conditional aggregation as following:
SQL> with dataa(name, class, type) as
2 (
3 select 'Alice', 'Math' ,'S' from dual union all
4 select 'Alice', 'Female' ,'A' from dual union all
5 select 'Bob', 'Anthropology' ,'S' from dual union all
6 select 'Bob', 'Male' ,'A' from dual union all
7 select 'Charlie', 'Science' ,'S' from dual union all
8 select 'Charlie', 'Tennis' ,'A' from dual union all
9 select 'Charlie', 'Male' ,'A' from dual union all
10 select 'Do', 'Math' ,'S' from dual union all
11 select 'Do', 'Female' ,'A' from dual union all
12 select 'Do', 'Tennis' ,'A' from dual union all
13 select 'Elmer', 'Male' ,'A' from dual union all
14 select 'Elmer', 'Science' ,'S' from dual
15 ),
16 -- your query starts from here
17 CTE AS
18 (
19 SELECT S.CLASS AS CLASS,
20 COALESCE(SUM(CASE WHEN A.CLASS = 'Female' THEN 1 END),0) AS Female,
21 COALESCE(SUM(CASE WHEN A.CLASS = 'Male' THEN 1 END),0) AS Male,
22 COALESCE(SUM(CASE WHEN A.CLASS = 'Tennis' THEN 1 END),0) AS Tennis,
23 COALESCE(SUM(CASE WHEN A.CLASS IN ('Female','Male','Tennis') THEN 1 END),0) AS TOTAL
24 FROM DATAA S JOIN DATAA A ON (A.name = S.name)
25 WHERE S.type = 'S' AND A.TYPE = 'A'
26 GROUP BY S.CLASS
27 )
28 SELECT CLASS, Female, Male, Tennis, TOTAL FROM CTE
29 UNION ALL
30 SELECT 'Total' AS CLASS, SUM(Female), SUM(Male), SUM(Tennis), SUM(TOTAL) FROM CTE;
CLASS FEMALE MALE TENNIS TOTAL
------------ ---------- ---------- ---------- ----------
Science 0 2 1 3
Anthropology 0 1 0 1
Math 2 0 1 3
Total 2 3 2 7
SQL>
Cheers!!
You have the destination "row" value and the destination "column" value in separate rows. In order to do a GROUP BY CUBE, you need to put them together in the same input row using a JOIN. So do a JOIN, then a GROUP BY CUBE, then a PIVOT. To keep things straight, use the GROUPING function to see when you are grouping "rows" and when you are grouping "columns". This becomes a lot of work, and you wind up getting NULLs in some places where you want zeroes. Here is the solution for curiosity's sake:
with data(name, class, type) as (
select 'Alice', 'Math' ,'S' from dual union all
select 'Alice', 'Female' ,'A' from dual union all
select 'Bob', 'Anthropology' ,'S' from dual union all
select 'Bob', 'Male' ,'A' from dual union all
select 'Charlie', 'Science' ,'S' from dual union all
select 'Charlie', 'Tennis' ,'A' from dual union all
select 'Charlie', 'Male' ,'A' from dual union all
select 'Do', 'Math' ,'S' from dual union all
select 'Do', 'Female' ,'A' from dual union all
select 'Do', 'Tennis' ,'A' from dual union all
select 'Elmer', 'Male' ,'A' from dual union all
select 'Elmer', 'Science' ,'S' from dual
)
, joined_data as (
select s.class row_class, a.class col_class
from data s
join data a
on s.type = 'S' and a.type = 'A' and s.name = a.name
)
, cubed_data as (
select case grouping(col_class) when 1 then 'Total' else col_class end col_class,
row_class, grouping(row_class) gr_row, count(*) cnt
from joined_data
group by cube(row_class, col_class)
)
select case gr_row when 1 then 'Total' else row_class end " ",
"Female", "Male", "Tennis", "Total"
from cubed_data
pivot(
sum(cnt) for col_class in (
'Female' as "Female",'Male' as "Male",'Tennis' as "Tennis",'Total' as "Total"
)
)
order by gr_row, row_class;
Female Male Tennis Total
------------ ---------- ---------- ---------- ----------
Anthropology 1 1
Math 2 1 3
Science 2 1 3
Total 2 3 2 7
Regards,
Stew
For this requirement, I prefer the solution by Tejash to the PIVOT demonstration in my other answer. However, I would suggest using ROLLUP instead of UNION ALL. Two lines are changed and the UNION ALL is left out.
with data(name, class, type) as (
select 'Alice', 'Math' ,'S' from dual union all
select 'Alice', 'Female' ,'A' from dual union all
select 'Bob', 'Anthropology' ,'S' from dual union all
select 'Bob', 'Male' ,'A' from dual union all
select 'Charlie', 'Science' ,'S' from dual union all
select 'Charlie', 'Tennis' ,'A' from dual union all
select 'Charlie', 'Male' ,'A' from dual union all
select 'Do', 'Math' ,'S' from dual union all
select 'Do', 'Female' ,'A' from dual union all
select 'Do', 'Tennis' ,'A' from dual union all
select 'Elmer', 'Male' ,'A' from dual union all
select 'Elmer', 'Science' ,'S' from dual
)
SELECT case grouping(S.CLASS) when 1 then 'Total' else s.class end AS CLASS,
COALESCE(SUM(CASE WHEN A.CLASS = 'Female' THEN 1 END),0) AS Female,
COALESCE(SUM(CASE WHEN A.CLASS = 'Male' THEN 1 END),0) AS Male,
COALESCE(SUM(CASE WHEN A.CLASS = 'Tennis' THEN 1 END),0) AS Tennis,
count(*) AS TOTAL
FROM DATA S JOIN DATA A ON (A.name = S.name)
WHERE S.type = 'S' AND A.TYPE = 'A'
GROUP BY rollup(S.CLASS);
CLASS FEMALE MALE TENNIS TOTAL
------------ ---------- ---------- ---------- ----------
Anthropology 0 1 0 1
Math 2 0 1 3
Science 0 2 1 3
Total 2 3 2 7
Regards,
Stew

Oracle Between return is not expected

I have a varchar colunm. with content
ColumnX
________
ABC
DEF
1
2
3
4
40
50
I need to get number between 1 and 4. SO i have this SQL
SELECT columnX
FROM table
WHERE regexp_substr(columnX, '[[:digit:]]') BETWEEN 1 and 4;
But my result i get is 1,2,3,4 and 40.
What shall I do to get it right?
You have to use a quantifier to match more than 1 character. In this case, I used the + quantifier, which matches one or more occurrences of the characters (digits, in this case).
Try this:
WITH
test_data AS
(SELECT 'ABC' AS columnX FROM dual
UNION ALL SELECT '1' FROM dual
UNION ALL SELECT '2' FROM dual
UNION ALL SELECT '3' FROM dual
UNION ALL SELECT '4' FROM dual
UNION ALL SELECT '40' FROM dual
UNION ALL SELECT '50' FROM dual
)
SELECT columnX
FROM test_data
WHERE regexp_substr(columnX, '[[:digit:]]+') BETWEEN 1 AND 4;
Output:
COLUMNX
-------
1
2
3
4