Get data based on condition in oracle sql - sql

My table
loads(Unique) Value
T123 11
T234 9.5
T456 15
T678 35
T345 3.7
Want I want
count(values<=10) count(values>10 &<=20) count(values>20)
2 2 1
I tried to use CASE but don't know the usage

CASE yes; not with COUNT but with SUM:
SQL> with test (loads, value) as
2 (select 't123', 11 from dual union all
3 select 't234', 9.5 from dual union all
4 select 't456', 15 from dual union all
5 select 't678', 35 from dual union all
6 select 't345', 3.7 from dual
7 )
8 select
9 sum(case when value <= 10 then 1 end) cnt_1,
10 sum(case when value > 10 and value <= 20 then 1 end) cnt_2,
11 sum(case when value > 20 then 1 end) cnt_3
12 from test;
CNT_1 CNT_2 CNT_3
---------- ---------- ----------
2 2 1
SQL>

Use conditional aggregation as
select coalesce(sum(case when value<=10 then 1 end),0) as "values<=10",
coalesce(sum(case when value>10 and value<=20 then 1 end),0) as "values>10value<20",
coalesce(sum(case when value>20 then 1 end),0) as "values>20"
from your_table;

Related

SQL Query To transform rows into columns with additional calculated column [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 9 days ago.
This post was edited and submitted for review 8 days ago and failed to reopen the post:
Original close reason(s) were not resolved
Improve this question
WIth sample data like here:
DATE Count Code
02-JAN-2023 25 A
03-JAN-2023 10 A
05-JAN-2023 15 A
01-JAN-2023 5 B
02-JAN-2023 20 B
04-JAN-2023 6 B
05-JAN-2023 9 B
I need to transform rows with code = 'A' or 'B' into A and B columns and to create another column to calculate the difference between A and B (DIFF = A - B). Empty values considered as 0. The result should be grouped and ordered by DATE.
The expected result should be:
Date A B DIFFERENCE(A-B)
01-JAN-2023 5 -5
02-JAN-2023 25 20 5
03-JAN-2023 10 10
04-JAN-2023 6 -6
05-JAN-2023 15 9 6
You need a conditional aggregation to get the desired result -
SELECT `date`, MAX(CASE WHEN CODE = 'A' THEN `count` ELSE NULL END) A,
MAX(CASE WHEN CODE = 'B' THEN `count` ELSE NULL END) B,
NVL(MAX(CASE WHEN CODE = 'A' THEN `count` ELSE NULL END), 0) -
NVL(MAX(CASE WHEN CODE = 'B' THEN `count` ELSE NULL END), 0) difference
FROM your_table
GROUP BY `date`;
Demo.
Conditional aggregation, yes - but most probably different aggregation (than Ankit suggested), I believe - sum instead of max. With sample data you posted it really doesn't matter, but there probably are some more rows, maybe even for the same date so max won't return correct result.
Instead of NVL function (Ankit used), consider using else 0 in case expression.
Also, you shouldn't (and you probably didn't) name columns using reserved words; date is reserved for datatype, so either column name isn't that, or you enclosed it into double quotes (which is mostly always bad idea).
Although you can do it in the same select statement, somewhat cleaner option is to use a subquery or a CTE (as my example shows); result will be just the same, but this is easier to read.
Sample data:
SQL> select * from test;
DATUM COUNT CODE
--------- ---------- -----
02-JAN-23 25 A
03-JAN-23 10 A
05-JAN-23 15 A
01-JAN-23 5 B
02-JAN-23 20 B
04-JAN-23 6 B
05-JAN-23 9 B
7 rows selected.
Query:
SQL> with temp as
2 (select datum,
3 sum(case when code = 'A' then count else 0 end) as count_a,
4 sum(case when code = 'B' then count else 0 end) as count_b
5 from test
6 group by datum
7 )
8 select datum, count_a, count_b,
9 count_a - count_b as diff
10 from temp
11 order by datum;
DATUM COUNT_A COUNT_B DIFF
--------- ---------- ---------- ----------
01-JAN-23 0 5 -5
02-JAN-23 25 20 5
03-JAN-23 10 0 10
04-JAN-23 0 6 -6
05-JAN-23 15 9 6
SQL>
You could use PIVOT to get your expected result:
Select A_DATE, COL_A, COL_B, Nvl(COL_A, 0) - Nvl(COL_B, 0) "DIFF_A_B"
From ( Select A_DATE, CNT, CODE From tbl )
PIVOT ( MAX(CNT) FOR CODE IN('A' "COL_A", 'B' "COL_B") )
Order By A_DATE
...
WITH -- Sample data
tbl AS
(
Select To_Date('02-JAN-2023', 'dd-MON-yyyy') "A_DATE", 25 "CNT", 'A' "CODE" From Dual Union All
Select To_Date('03-JAN-2023', 'dd-MON-yyyy') "A_DATE", 10 "CNT", 'A' "CODE" From Dual Union All
Select To_Date('05-JAN-2023', 'dd-MON-yyyy') "A_DATE", 15 "CNT", 'A' "CODE" From Dual Union All
Select To_Date('01-JAN-2023', 'dd-MON-yyyy') "A_DATE", 5 "CNT", 'B' "CODE" From Dual Union All
Select To_Date('02-JAN-2023', 'dd-MON-yyyy') "A_DATE", 20 "CNT", 'B' "CODE" From Dual Union All
Select To_Date('04-JAN-2023', 'dd-MON-yyyy') "A_DATE", 6 "CNT", 'B' "CODE" From Dual Union All
Select To_Date('05-JAN-2023', 'dd-MON-yyyy') "A_DATE", 9 "CNT", 'B' "CODE" From Dual
)
R e s u l t :
A_DATE COL_A COL_B DIFF_A_B
--------- ---------- ---------- ----------
01-JAN-23 5 -5
02-JAN-23 25 20 5
03-JAN-23 10 10
04-JAN-23 6 -6
05-JAN-23 15 9 6

split column value into multiple columns in oracle sql

I have data into table like this:
I want the output like this:
how we can achive this with orace sql statment.Kindly assist.
instr(comp,':')
yields the first colon occurrence
instr(comp,':',1,2)
yields the second colon occurrence, and so forth
After that, its just math with combinations of INSTR on : and |
For example
substr(comp,2,instr(comp,':')-2)
for the first column before the colon
substr(comp,instr(comp,':')+1,instr(comp,'|')-instr(comp,':')-1)
for the first element after the colon and before the bar
and so forth.
This is one option; not dynamic at all as you'll have to know how many items there are so that you could adjust the final query. Read comments within code.
SQL> with test (id, component) as
2 -- sample data
3 (select 1, '|TD-2-2720A-NVE-C:2|TD-2-2720A-TPM-C:2|TD-PREM-NLSAS-01-PR:480|TD-ONTAP-NLSAS-01-PR:480' from dual union all
4 select 2, '|TD-2-A220A-TPM-C:2|DD-FLASH-PREM-01-PR:115|DD-FLASH-ONTAP-01-PR:115' from dual union all
5 select 5, '|TD-2-2650A-NVE-C:2|TD-2-2650A-TPM-C:2' from dual
6 ),
7 temp as
8 -- split each component into rows, separated by the pipe character
9 (select id,
10 column_value cv,
11 regexp_substr(component, '[^|]+', 1, column_value) comp
12 from test cross join
13 table(cast(multiset(select level from dual
14 connect by level <= regexp_count(component, '\|')
15 ) as sys.odcinumberlist))
16 )
17 -- final result
18 select id,
19 max(case when cv = 1 then substr(comp, 1, instr(comp, ':') - 1) end) as comp_1,
20 max(case when cv = 1 then regexp_substr(comp, '\d+$') end) as cnt_1,
21 --
22 max(case when cv = 2 then substr(comp, 1, instr(comp, ':') - 1) end) as comp_2,
23 max(case when cv = 2 then regexp_substr(comp, '\d+$') end) as cnt_2,
24 --
25 max(case when cv = 3 then substr(comp, 1, instr(comp, ':') - 1) end) as comp_3,
26 max(case when cv = 3 then regexp_substr(comp, '\d+$') end) as cnt_3,
27 --
28 max(case when cv = 4 then substr(comp, 1, instr(comp, ':') - 1) end) as comp_4,
29 max(case when cv = 4 then regexp_substr(comp, '\d+$') end) as cnt_4
30 from temp
31 group by id
32 order by id;
ID COMP_1 CNT_1 COMP_2 CNT_2 COMP_3 CNT_3 COMP_4 CNT_4
--- -------------------- ----- -------------------- ----- -------------------- ----- -------------------- -----
1 TD-2-2720A-NVE-C 2 TD-2-2720A-TPM-C 2 TD-PREM-NLSAS-01-PR 480 TD-ONTAP-NLSAS-01-PR 480
2 TD-2-A220A-TPM-C 2 DD-FLASH-PREM-01-PR 115 DD-FLASH-ONTAP-01-PR 115
5 TD-2-2650A-NVE-C 2 TD-2-2650A-TPM-C 2
SQL>

Query for Joining three tables with row value as column header

I have three tables as shown below.
TABLE1 : tb_subject
subject_id subject_name
1 English
2 Maths
3 Science
Table2 : tb_student
subject_id student_id
1 AA
1 BB
2 CC
3 DD
3 EE
Table3 : tb_student_score
student_id score conducted_month_number
AA 20 2
BB 30 3
CC 50 4
AA 80 4
DD 50 6
BB 10 2
EE 40 3
Result should be
conducted_month_number SUM(subject_id1) SUM(subject_id2) SUM(subject_id3)
1 0 0 0
2 30 0 0
3 30 0 40
4 80 50 0
5 0 0 0
6 0 0 60
7 0 0 0
8 0 0 0
9 0 0 0
10 0 0 0
11 0 0 0
12 0 0 0
How to write a select query for this? Can add all month number that is not stored in table as like in the resulted output?
You should be able to use case when to sum for each subject individually:
SELECT conducted_month_number,
SUM(CASE b.subject_id WHEN 1 THEN a.score ELSE 0 END) AS English,
SUM(CASE b.subject_id WHEN 2 THEN a.score ELSE 0 END) AS Maths,
SUM(CASE b.subject_id WHEN 3 THEN a.score ELSE 0 END) AS Science
FROM tb_student_score AS a
JOIN tb_student AS b ON b.student_id = a.student_id
GROUP BY conducted_month_number
ORDER BY conducted_month_number;
However, this alone will not ensure you have results for values of conducted_month_number that don't exist - if this is an issue, you could simply create a dummy student with a score of 0 for each month.
Edit: I noticed some comments posted around the same time I submitted my answer - if you want the number of summation columns to be variable based on the values of rows in the tb_subject table, you will not find the relational model of SQL to be well suited for that task. However, you can easily go back and update your query to include any new subjects you may add later on.
Have added dummy values of 1 to 12 months using union statement and later on did group by on them to calculate total scores.
Try this:-
Select conducted_month_number ,
sum(case when subject_id=1 then score else 0 end) as sum_subject_id1,
sum(case when subject_id=2 then score else 0 end) as sum_subject_id2,
sum(case when subject_id=3 then score else 0 end) as sum_subject_id3
from
(
Select a.conducted_month_number ,subject_id,score
from
tb_student_score a
inner join
tb_student b
on a.student_id=b.student_id
union
select 1,' ',0 from tb_student_score
union
select 2,' ',0 from tb_student_score
union
select 3,' ',0 from tb_student_score
union
select 4,' ',0 from tb_student_score
union
select 5,' ',0 from tb_student_score
union
select 6,' ',0 from tb_student_score
union
select 7,' ',0 from tb_student_score
union
select 8,' ',0 from tb_student_score
union
select 9,' ',0 from tb_student_score
union
select 10,' ',0 from tb_student_score
union
select 11,' ',0 from tb_student_score
union
select 12,' ',0 from tb_student_score
)a
group by conducted_month_number
My Output
conducted_month_number sum_subject_id1 sum_subject_id2 sum_subject_id3
1 0 0 0
2 30 0 0
3 30 0 40
4 80 50 0
5 0 0 0
6 0 0 50
7 0 0 0
8 0 0 0
9 0 0 0
10 0 0 0
11 0 0 0
12 0 0 0

Queryin One-to-Many same table in Oracle

I have a below table with sample data as below:
prod seq catid
-------------------
prod1 0 10
prod1 1 20
prod1 2 30
prod1 3 40
prod2 0 10
prod3 0 10
prod3 1 20
prod3 2 30
prod4 0 10
I need to query above table based on catid column.
Ex: If i query with catid - 10 then i need to get all products(here prod2, prod3, prod4) which has only catid as 10 all the other should be excluded.
Same way if i query with catid = 10 20 30 then i need to get output as prod3, if i query with catid as 10 20 30 40 then my output should be prod1.
SELECT * FROM table WHERE catid = ALL (1,2,3,4,5)
I tried using ALL in my query but i am not able to get desired output, please help.
You can get the expected prod value using the query:
SELECT prod
FROM mytable
GROUP BY prod
HAVING COUNT(DISTINCT catId) = 5 AND
COUNT(CASE WHEN catId NOT IN (1,2,3,4,5) THEN 1 END) = 0
The above query returns products having 5 distinct catId values. None of these values doesn't belong to (1,2,3,4,5) set of values.
One way to do this is using conditional aggregation:
SELECT prod
FROM yourTable
GROUP BY prod
HAVING SUM(CASE WHEN catid = 1 THEN 1 ELSE 0 END) > 0 AND
SUM(CASE WHEN catid = 2 THEN 1 ELSE 0 END) > 0 AND
SUM(CASE WHEN catid = 3 THEN 1 ELSE 0 END) > 0 AND
SUM(CASE WHEN catid = 4 THEN 1 ELSE 0 END) > 0 AND
SUM(CASE WHEN catid = 5 THEN 1 ELSE 0 END) > 0
Here's a way that is more generalised - it relies on you passing in a string to the query consisting of a comma separated list (no spaces) into the query:
WITH your_table AS (SELECT 'prod1' prod, 0 seq, 10 catid FROM dual UNION ALL
SELECT 'prod1' prod, 0 seq, 20 catid FROM dual UNION ALL
SELECT 'prod1' prod, 0 seq, 30 catid FROM dual UNION ALL
SELECT 'prod1' prod, 0 seq, 40 catid FROM dual UNION ALL
SELECT 'prod2' prod, 0 seq, 10 catid FROM dual UNION ALL
SELECT 'prod3' prod, 0 seq, 10 catid FROM dual UNION ALL
SELECT 'prod3' prod, 0 seq, 20 catid FROM dual UNION ALL
SELECT 'prod3' prod, 0 seq, 30 catid FROM dual UNION ALL
SELECT 'prod4' prod, 0 seq, 10 catid FROM dual)
-- end of mimicking your table with sample data in it
-- see sql below:
SELECT yt.prod
FROM your_table yt
GROUP BY yt.prod
HAVING min(CASE WHEN ','||:p_string_to_compare||',' LIKE '%,'||yt.catid||',%' THEN 'Y'
ELSE 'N'
END) = 'Y'
AND count(*) = regexp_count(:p_string_to_compare, ',') + 1;
With :p_string = '10,40,30,20':
PROD
-----
prod1
With :p_string = '10,20,30':
PROD
-----
prod3
With :p_string = '10, 20':
no rows returned
With :p_string = '10':
PROD
-----
prod4
prod2

How to turn repeated ranking(1-5) row data to column data in TSQL

I have a table data:
ID Sale Weekday
1 12 1
2 15 2
3 16 3
4 17 4
5 18 5
6 11 1
7 13 2
8 14 3
9 15 4
10 20 5
11 25 1
12 14 2
13 18 3
14 21 4
15 11 5
.. ..
I'd like to turn it into:
Mo Tu We Th Fr
12 15 16 17 18
11 13 14 15 20
25 14 18 21 11
..
Thank you!
Try this
SELECT SUM(case when Weekday = 1 then Sale else 0 end) as mn,
SUM(case when Weekday = 2 then Sale else 0 end) as Tu,
SUM(case when Weekday = 3 then Sale else 0 end) as We,
SUM(case when Weekday = 4 then Sale else 0 end) as Th,
SUM(case when Weekday = 5 then Sale else 0 end) as Fr
FROM
(
SELECT *,
Row_number()OVER(partition by weekday ORDER BY ID ) as seq_no
FROM tablename
) A
Group by seq_no
As mentioned in sample data if your table has all 5 days for all the week
SELECT SUM(case when Weekday = 1 then Sale else 0 end) as mn,
SUM(case when Weekday = 2 then Sale else 0 end) as Tu,
SUM(case when Weekday = 3 then Sale else 0 end) as We,
SUM(case when Weekday = 4 then Sale else 0 end) as Th,
SUM(case when Weekday = 5 then Sale else 0 end) as Fr
FROM
(
SELECT *,
( ( Row_number()OVER(ORDER BY ID ) - 1 ) / 5 ) + 1 seq_no
FROM tablename
) A
Group by seq_no
SQL FIDDLE DEMO
You could use the pivot operator together with a partitioned row_number like this:
select
max([1]) as 'Mo',
max([2]) as 'Tu',
max([3]) as 'We',
max([4]) as 'Th',
max([5]) as 'Fr'
from
(
select *, row_number() over (partition by weekday order by id) rn
from your_table
) a
pivot (max(sale) for weekday in ([1],[2],[3],[4],[5])) p
group by rn;