Oracle : Multiple Pivot columns on the Same numeric column - sql

I have the following Data:
Vehicle ID Region District City Revenue
X50 Southwest FLD Miami 50
X50 Northeast NYC Rochester 70
X70 Southwest FLD Miami 20
I want to pivot the Region, District and City with the Revenue column.
Output:
Vehicle ID Southwest_R Northeast_R FLD_R NYC_R Miami_R Rochester_R
X50 50 0 50 0 50 0
X50 0 75 0 75 0 75
X70 20 0 25 0 25 0
The problem I am facing is to add those pivots in a single query.
Select * from table
Pivot( SUM(Revenue) for Region IN ('Southwest' Southwest_R, 'Northeast' Northeast_R))
in this query if I add District and City, it throws error.

Hmmm . . . This looks like conditional logic not aggregation:
select vehicle_id,
(case when region = 'Southwest' then revenue else 0 end) as southwest_r,
(case when region = 'Northeast' then revenue else 0 end) as northeast_r,
(case when district = 'District' then revenue else 0 end) as fld_r,
(case when district = 'NYC' then revenue else 0 end) as nyc_r,
. . .
from t;
If you can have multiple rows per vehicle, then you can use aggregation:
select vehicle_id,
sum(case when region = 'Southwest' then revenue else 0 end) as southwest_r,
sum(case when region = 'Northeast' then revenue else 0 end) as northeast_r,
sum(case when district = 'District' then revenue else 0 end) as fld_r,
sum(case when district = 'NYC' then revenue else 0 end) as nyc_r,
. . .
from t
group by vehicle_id;

Related

How to transpose columns?

I have this table
SKU
CITY
MOV
TYPE
UNI
IMP
1
116
49
Caducidad
3
203.889
1
116
48
Daño
3
203.889
1
116
47
Robo
NULL
NULL
And I'm trying to transpose 'Type' column to display something like this
SKU
CITY
TYPE_UNI_CADUCIDAD
TYPE_IMP_CADUCIDAD
TYPE_UNI_DAÑO
TYPE_IMP_DAÑO
TYPE_UNI_ROBO
TYPE_IMP_ROBO
1
116
3
203.889
3
203.889
NULL
NULL
I tried case and pivot but not really working
SELECT SKU, CITY,
case
when MOV=49 then sum(Total_Imp) end as Type_Imp_Caducidad ,
case
when MOV=48 then sum(Total_Imp) end as Type_Imp_Daño ,
case
when MOV=47 then sum(Total_Imp) end as Type_Imp_Robo
from #movimientos
where Id_Num_SKU=11466978
group by SKY, CITY, MOV
As both comentsalready told you you need to aggreate the hole CASE WHEN to have an aggregation function
SELECT SKU, CITY,
sum(case
when MOV=49 then Total_Imp end) as Type_Imp_Caducidad ,
sum(case
when MOV=48 then Total_Imp end) as Type_Imp_Daño ,
SUM(case
when MOV=47 then Total_Imp end) as Type_Imp_Robo
from #movimientos
where Id_Num_SKU=11466978
group by SKY, CITY
You need to remove MOV from the GROUP BY and then aggregate the whole CASE expression
SELECT
m.SKU,
m.CITY,
SUM(case when m.MOV = 49 then m.Total_Uni end) as Type_Uni_Caducidad,
SUM(case when m.MOV = 49 then m.Total_Imp end) as Type_Imp_Caducidad,
SUM(case when m.MOV = 48 then m.Total_Uni end) as Type_Uni_Daño,
SUM(case when m.MOV = 48 then m.Total_Imp end) as Type_Imp_Daño,
SUM(case when m.MOV = 47 then m.Total_Uni end) as Type_Uni_Robo,
SUM(case when m.MOV = 47 then m.Total_Imp end) as Type_Imp_Robo
from #movimientos m
where m.Id_Num_SKU = 11466978
group by
m.SKY,
m.CITY;

Getting sub-counts along with Average and Count

I have two tables...
MODULES ENROLMENTS
GroupNo StudentNo
Title GroupNo
Tutor CourseworkMark
DayNo ExamMark
Time
Room
Semester
I wish to create a view that displays the average mark achieved in coursework and exam for each module and also a count of the number of students who achieved >70, 60-69, 50-59, 40-49 and <40. Is this possible?
I have the average marks worked out with...
SELECT Title,
AVG(CourseworkMark) AS AverageCoursework,
AVG(ExamMark) AS AverageExam
FROM tblModules INNER JOIN tblEnrolments
ON tblModules.GroupNo = tblEnrolments.GroupNo
GROUP BY Title;
You can use a SUMmed CASE expression to do this;
SELECT Title,
AVG(CourseworkMark) AS AverageCoursework,
AVG(ExamMark) AS AverageExam,
SUM(CASE WHEN CourseworkMark > 70 THEN 1 ELSE 0 END) AS CourseworkMarkOver70,
SUM(CASE WHEN CourseworkMark BETWEEN 60 AND 70 THEN 1 ELSE 0 END) AS CourseworkMarkOver60To69,
SUM(CASE WHEN CourseworkMark BETWEEN 50 and 59 THEN 1 ELSE 0 END) AS CourseworkMarkOver50To59,
SUM(CASE WHEN CourseworkMark BETWEEN 40 and 49 THEN 1 ELSE 0 END) AS CourseworkMarkOver40To49,
SUM(CASE WHEN CourseworkMark < 40 THEN 1 ELSE 0 END) AS CourseworkMarkUnder40
FROM tblModules INNER JOIN tblEnrolments
ON tblModules.GroupNo = tblEnrolments.GroupNo
GROUP BY Title;

sum values with sql server

I have the following table test
id Actual Budget CA RUB_ID
-------------------------------------------
1 20 30 201 902
1 2 330 202 902
1 220 130 207 90
1 21 30 20 12
How can get the following result
Actual Budget CA
20 130 12
I need to do sum Actual if RUB_ID =902 , sum Budget if RUB_ID =90 ,sum CA if RUB_ID =12
select
id,
case
when RUB_ID = 902 then sum(Actual) AS Actual
else case
when RUB_ID = 90
then sum(Budget) as Budget
else case
when RUB_ID = 12 then sum(CA) as CA
FROM
TEST
group by
id
The query does not return what I am looking for , how can I modify it ?
Put your case statements inside the sum functions
select
sum(case when RUB_ID = 902 then Actual else 0 end) Actual,
sum(case when RUB_ID = 90 then Budget else 0 end) Budget,
sum(case when RUB_ID = 12 then CA else 0 end) CA
from test where RUB_ID IN (902,90,12)
if you want these results by id
select
id,
sum(case when RUB_ID = 902 then Actual else 0 end) Actual,
sum(case when RUB_ID = 90 then Budget else 0 end) Budget,
sum(case when RUB_ID = 12 then CA else 0 end) CA
from test where RUB_ID IN (902,90,12)
group by id
Something like this:
SELECT
id
, SUM(CASE RUB_ID WHEN 902 THEN Actual ELSE 0 END) as Actual
, SUM(CASE RUB_ID WHEN 90 THEN Budget ELSE 0 END) as Budget
, SUM(CASE RUB_ID WHEN 12 THEN CA ELSE 0 END) as CA
FROM TEST
GROUP BY id

sql subquery that collects from 3 rows

I have a huge database with over 4 million rows that look like that:
Customer ID Shop
1 Asda
1 Sainsbury
1 Tesco
2 TEsco
2 Tesco
I need to count customers that within last 4 weeks had shopped in all 3 shops Tesco Sainsbury and Asda. Can you please advice if its possible to do it with subqueries?
This is an example of a "set-within-sets" subquery. You can solve it with aggregation:
select customer_id
from Yourtable t
where <shopping date within last four weeks>
group by customer_id
having sum(case when shop = 'Asda' then 1 else 0 end) > 0 and
sum(case when shop = 'Sainsbury' then 1 else 0 end) > 0 and
sum(case when shop = 'Tesco' then 1 else 0 end) > 0;
This structure is quite flexible. So if you wanted Asda and Tesco but not Sainsbury, then you would do:
select customer_id
from Yourtable t
where <shopping date within last four weeks>
group by customer_id
having sum(case when shop = 'Asda' then 1 else 0 end) > 0 and
sum(case when shop = 'Sainsbury' then 1 else 0 end) = 0 and
sum(case when shop = 'Tesco' then 1 else 0 end) > 0;
EDIT:
If you want a count, then use this as a subquery and count the results:
select count(*)
from (select customer_id
from Yourtable t
where <shopping date within last four weeks>
group by customer_id
having sum(case when shop = 'Asda' then 1 else 0 end) > 0 and
sum(case when shop = 'Sainsbury' then 1 else 0 end) > 0 and
sum(case when shop = 'Tesco' then 1 else 0 end) > 0
) t

count(*) based on the gender condition

I have the following table in oracle10g.
state gender avg_sal status
NC M 5200 Single
OH F 3800 Married
AR M 8800 Married
AR F 6200 Single
TN M 4200 Single
NC F 4500 Single
I am trying to form the following report based on some condition. The report should look like the one below. I tried the below query but count(*) is not working as expected
state gender no.of males no.of females avg_sal_men avg_sal_women
NC M 10 0 5200 0
OH F 0 5 0 3800
AR M 16 0 8800 0
AR F 0 12 0 6200
TN M 22 0 4200 0
NC F 0 8 0 4500
I tried the following query but I am not able to count based onthe no.of males and no.of females..
select State, "NO_OF MALES", "$AVG_sal", "NO_OF_FEMALES", "$AVG_SAL_FEMALE"
from(
select State,
to_char(SUM((CASE WHEN gender = 'M' THEN average_price ELSE 0 END)),'$999,999,999') as "$Avg_sal_men,
to_char(SUM((CASE WHEN gender = 'F' THEN average_price ELSE 0 END)), '$999,999,999') as "$Avg_sal_women,
(select count (*) from table where gender='M')"NO_OF MALES",
(select count (*) from table where gender='F')"NO_OF_FEMALES"
from table group by State order by state);
You can use case as an expression (which you already know...). And the subquery is unnecessary.
select State
, sum(case gender when 'M' then 1 else 0 end) as "no.of males"
, sum(case gender when 'F' then 1 else 0 end) as "no.of females"
, to_char(
SUM(
(
CASE
WHEN gender = 'M' THEN average_price
ELSE 0
END
)
)
, '$999,999,999'
) as "Avg_sal_men",
to_char(SUM((CASE WHEN gender = 'F' THEN average_price ELSE 0 END))
,'$999,999,999'
) as "Avg_sal_women"
from table
group by State;
You are Conting by this sub-query select count (*) from table where gender='M' which always count the total number of male in your whole table....and you are doing same for counting female...
So you Can Try like this...
select State, "NO_OF MALES", "$AVG_sal", "NO_OF_FEMALES", "$AVG_SAL_FEMALE"
from(
select State,
to_char(SUM((CASE WHEN gender = 'M' THEN average_price ELSE 0 END)),'$999,999,999') as "$Avg_sal_men",
to_char(SUM((CASE WHEN gender = 'F' THEN average_price ELSE 0 END)), '$999,999,999') as "$Avg_sal_women,
Sum(Case when gender='M' then 1 else 0 end) "NO_OF MALES",
Sum(Case when gender='F' then 1 else 0 end) "NO_OF_FEMALES"
from table group by State order by state);
Try the following.
select state
,sum(case when gender = 'M' then 1 else 0 end) as nof_males
,sum(case when gender = 'F' then 1 else 0 end) as nof_females
,avg(case when gender = 'M' then average_price end) as avg_sal_male
,avg(case when gender = 'F' then average_price end) as avg_sal_female
from table
group
by state;
..add formatting as required.