Oracle SQL group by rollup and ratio_to_report - sql

I have a table like this
I have a query to show the number of vehicles manufactured in each region and the percentage of vehicles manufactured in the region
select
COALESCE(Region, 'Total') AS Region,
count(veh_vin) as "Total Vehicles Manufactured",
round(ratio_to_report(count(veh_vin)) over(),2) as rr
from(
select
case
when substr(veh_vin,1,1) between 'A' and 'C' then 'Africa'
when substr(veh_vin,1,1) between 'J' and 'R' then 'Asia'
when substr(veh_vin,1,1) between 'S' and 'Z' then 'Europe'
when substr(veh_vin,1,1) between '1' and '5' then 'North America'
when substr(veh_vin,1,1) between '6' and '7' then 'Oceania'
when substr(veh_vin,1,1) between '8' and '9' then 'South America'
else 'Unknown'
end as Region,
veh_vin
from vehicle
)
group by ROLLUP(Region)
order by count(veh_vin), Region;
which gives me the following result
But I want something like this, which exclude the total when calculating the percentage.
Is it possible to achieve it with ratio_to_report? If not, how could I alter the query?

I prefer an explicit calculation. You can adjust the calculation (either way) for the aggregated row:
select coalesce(Region, 'Total') AS Region,
count(*) as "Total Vehicles Manufactured",
round(count(*) * 1.0 / sum(case when grouping_id(region) = 0 then count(*) end) over (), 2) as rr
from . . .
The same idea doesn't quite work with ratio_to_report() -- because it returns NULL instead of 1. But you could use:
coalesce(round(ratio_to_report(case when grouping_id(region) = 0 then count(*) end) over(), 2), 1) as rr
Here is a little db<>fiddle.

Related

How to use count using case statements

I am trying to get the total number of items and categorize them using case statements. However, I keep getting the items (which are now categorized as "inv") repeated (e.g. "clothes" is repeated 4 times, each with a count of 1 instead of just once with the total count of 4) - how do I get rid of that? I included the desired outcome below.
Here's my table:
TABLE BOX1
ITEM_NO ITEM DATE
===================================
3130C MARVEL_SHIRT 01-JAN-17
1845C SPONGEBOB_BOXERS 03-DEC-18
A900C CK_COAT 04-DEC-18
A988C RIDER_JEANS 11-JAN-19
L350T BARBIE 23-NOV-18
L129T LEGO 09-OCT-18
LXZYT BATMAN_FIG1 12-JAN-19
My code:
select (case substr (item_no,5,1)
when 'C' then 'clothes'
when 'T' then 'toys' else 'misc' end) inv,
count(item_no) total
from box1 where date <= sysdate
group by item
Desired Outcome:
INV TOTAL
===============
CLOTHES 4
TOYS 3
You need to group by your categories.
SELECT CASE substr(item_no, 5, 1)
WHEN 'C' THEN
'clothes'
WHEN 'T' THEN
'toys'
ELSE
'misc'
END inv,
count(item_no) total
FROM box1
WHERE date <= sysdate
GROUP BY CASE substr(item_no, 5, 1)
WHEN 'C' THEN
'clothes'
WHEN 'T' THEN
'toys'
ELSE
'misc'
END;
Becasue of the nature of your expression, you only need to aggregate on substr():
select (case substr(item_no, 5, 1)
when 'C' then 'clothes'
when 'T' then 'toys'
else 'misc'
end) inv,
count(item_no) total
from box1
where date <= sysdate
group by substr(item_no, 5, 1);

SQL - Counting column categories

I am new to SQL, but I am trying to create a report where I can count the food purchases made by students and categorized by the Race/Ethnicity and choice of food every year.
For Example:
2012 2013
Student Vegetarian Meat Unknown Vegetarian Meat Unknown
---------------------------------------------------------------------------
Black 5 Purchases 4 Purchases 3 Purchases 5 Purchases etc
White 4 Purchases 3 Purchases 3 Purchases etc etc
Asian 4 Purchases 1 Purchases 6 Purchases etc etc
While I was able to get the races in one column, I am lost at getting the count on different food categories. All I know is that the pur_type is classified as "Veg", "Me" and "Ukt" for Vegetarian, Meat and unknown purchase.
Here is what I tried to do:
select
DATEPART(YYYY,pur_date) AS Year
,count (*) Citations
, race
, sex
, ethnicity
, GETDATE() as YTD
,COUNT(CASE WHEN pur_type = 'Veg' THEN 'Vegetarian'
WHEN pur_type = 'Me' THEN 'Meat'
WHEN pur_type = 'UKt' THEN 'Unknown'
ELSE ''
End as "Type"
, CASE
WHEN race='W' and sex='M' and ethnicity <> 'H' THEN 'Caucasian(Male)'
WHEN race='W' and sex='F' and ethnicity <> 'H' THEN 'Caucasian(Female)'
WHEN race='B' and sex='M' and ethnicity <> 'H' THEN 'African-American(Male)'
WHEN race='B' and sex='F' and ethnicity <> 'H' THEN 'African-American(Female)'
--WHEN race='A' and sex='M' and ethnicity <> 'H' THEN 'Pacific Islander(Male)'
--WHEN race='A' and sex='F' and ethnicity <> 'H' THEN 'Pacific Islander(Female)'
WHEN race='A' and sex='M' and ethnicity <> 'H' THEN 'Asian(Male)'
WHEN race='A' and sex='F' and ethnicity <> 'H' THEN 'Asian(Female)'
ELSE ''
END as 'Race'
from purchase
join purcharge
on purchase.purchaseid = purcharge.purchaseid
where pur_date between #startDate and dateadd(DD, +1, GETDATE())
and pur_type in ('Veg', 'Me', 'UKt')
GROUP BY DATEPART(YYYY,pur_date), race, sex, ethnicity , pur_type
ORDER BY 'Year','Race/Sex'
Looking for guidance. I am thinking maybe a sub query would work, maybe more case statements, but I am not able to have a count of how many food categories were purchased by students in each ethnic group.
Also,I am trying to get these results in an SSRS report so it may look slightly different in a SQL query.
You could look into the PIVOT statement but if it was me, I'd do more CASE...WHEN and then GROUP BY.
So, instead of
COUNT(CASE WHEN pur_type = 'Veg' THEN 'Vegetarian' (etc) AS Type
I'd do
SUM(CASE WHEN pur_type = 'Veg' THEN 1 ELSE 0) AS Vegetarian
etc.
This isn't really the best way to create a report, but if you need to use SQL (as opposed to having another layer of code to generate the report from a bunch of subsets of data), you could use subqueries. Without seeing the table structure, it is hard to write the query, but it could be something like this:
Select
race
, (select count(*) from purchase where pur_type = 'Veg' and race = race) as Vegetables
, (select count(*) from purchase where pur_type = 'Me' and race = race) as Meat
, (select count(*) from purchase where pur_type = 'UKt' and race = race) as Unknown
From purchase
Like I said, this isn't ideal, but it would get your the distinct subsets of data.
I don't know what your data looks like and havn't got time to build sample data but I think you've actually just got a typo or two..
SELECT
DATEPART(YYYY,pur_date) AS [Year]
,count (*) Citations
, race
, sex
, ethnicity
, GETDATE() as YTD
, COUNT(*) AS [PurchaseCount]
, CASE
WHEN pur_type = 'Veg' THEN 'Vegetarian'
WHEN pur_type = 'Me' THEN 'Meat'
WHEN pur_type = 'UKt' THEN 'Unknown'
ELSE ''
End as [Type]
, CASE
WHEN race='W' and sex='M' and ethnicity <> 'H' THEN 'Caucasian(Male)'
WHEN race='W' and sex='F' and ethnicity <> 'H' THEN 'Caucasian(Female)'
WHEN race='B' and sex='M' and ethnicity <> 'H' THEN 'African-American(Male)'
WHEN race='B' and sex='F' and ethnicity <> 'H' THEN 'African-American(Female)'
--WHEN race='A' and sex='M' and ethnicity <> 'H' THEN 'Pacific Islander(Male)'
--WHEN race='A' and sex='F' and ethnicity <> 'H' THEN 'Pacific Islander(Female)'
WHEN race='A' and sex='M' and ethnicity <> 'H' THEN 'Asian(Male)'
WHEN race='A' and sex='F' and ethnicity <> 'H' THEN 'Asian(Female)'
ELSE ''
END as [Race]
FROM purchase
JOIN purcharge
ON purchase.purchaseid = purcharge.purchaseid
WHERE pur_date BETWEEN #startDate AND dateadd(DD, +1, GETDATE())
AND pur_type in ('Veg', 'Me', 'UKt')
GROUP BY DATEPART(YYYY,pur_date), race, sex, ethnicity , pur_type
ORDER BY [Year],[Race], [Type]
This will not give exactl what you asked for as it includes both race and gender in your [Race] case statement but maybe that's what you really wanted, I don't know...
Anyway, all I really changed was the count(*) as [PurchaseCount] , the following line's CASE statement and the GROUP BY clause. COUNT(*) will just give you the count of records that fall within your GROUP BY clause. In this case a count of records for every [Year], [Race] and [Type]

Divide and sum in SQL

I got this code and in this code I do a sum of the slow and fast driver. My Problem is I must divide this sum with the normal driver. I donĀ“t know how I can do a division in this statement:
Select *
FROM (
Select date as Datetime, tevent.name as Event, level = case
when levelname = 'High' then 'High'
when levelname = 'Normal' then 'Normal'
when shiftname = 'Low' then 'Low'
end, SUM(value) as sum
from tCount inner join tEvent ON tCount.eventid = tevent.id
where Name in ('Drive Fast', 'Drive Slow')
and date > getdate() -1
and tevent.Name in ('E01','E02','E03','E04','E05','E06','E07','E08')
and CalName = 'Drive'
group by tevent.name, date, levelname
) as s
PIVOT
(
SUM(sum)
FOR Event IN (E01,E02,E03,E04,E05,E06,E07,E08)
) as p
order by Datetime, level
And Then I put the same Select statement with the normal driver :
... from tCount inner join tEvent ON tCount.eventid = tevent.id
where Name in ('drive normal') ...
And I would like to make a division like this:
(Sum('drive fast' + 'drive slow')/Sum('drive normal')) * 100
There is a simpler way to include different cases in different sums inside a SQL statement: sum a case, like in the below calculation of percent:
Select ...
, SUM(case Name
when 'drive fast' then Value
when 'drive slow' then value
else 0 end)
/ SUM(case Name
when 'drive normal' then value
else 0 end) * 100 as percentage
from ...
where ...
group by ...;
As I lack data to test this code, I created a query on the CARS table SAS delivers as training material, implementing the same principle.
select Cylinders
, sum(case origin when 'USA' then EngineSize
when 'Asia' then EngineSize
else 0.0 end)
/ sum(case origin when 'Europe' then EngineSize
else 0.0 end)
* 100 as percentage
from sasHelp.cars
where Cylinders in (4, 5, 6, 12)
group by Cylinders

Can I add multiple summary rows in TSQL?

Goal: I am trying to add a summary row per school and I have this set up in Fiddle.
Needs:
List item I would like the enrollment totals for each year for Smith Elementary, Jones Elementary, etc.
List item I would also like to have summary rows for all levels (ES,MS,HS).
List item I would also like to have a grand total row for all levels.
Fiddle:
Is this possible in TSQL? I'm running SQL Server 2008 R2.
SELECT
schoolid,
sitename,
level,
area,
grade,
20122013ActualEnrollment ,
20132014ActualEnrollment,
20152016ActualEnrollment
FROM supportContacts
ORDER BY
CASE
WHEN Level= 'ES' THEN '1'
WHEN Level= 'MS' THEN '2'
WHEN Level= 'HS' THEN '3'
ELSE '4'
END
, SiteName
, CASE
WHEN Grade = 'K' THEN '1'
WHEN Grade = '1' THEN '2'
WHEN Grade = '2' THEN '3'
WHEN Grade = '3' THEN '4'
WHEN Grade = '4' THEN '5'
WHEN Grade = '5' THEN '6'
ELSE '7'
END
Output:
Thank you for your help!
Please realize this is pretty much a shot in the dark. I don't have the structure or sample data when writing this.
SELECT
SY12.[SchoolID]
, MAX(SY12.[SiteName])
, MAX(SY12.[Level])
, MAX(S.[AreaName])
, SY12.[Grade]
, SY12.[TotalEnrollment] AS '2012-2013 Actual Enrollment'
, SY13.[TotalEnrollment] AS '2013-2014 Actual Enrollment'
, P.[2016] AS '2015-2016 Projected Enrollment'
FROM
[2012Cycle1_Data] AS SY12
LEFT OUTER JOIN
[2013Cycle1_Data] AS SY13 ON SY12.SchoolID = SY13.[SchoolID]
AND SY12.Grade = SY13.Grade
LEFT OUTER JOIN
[Projections] AS P ON SY12.SchoolID = P.[SchoolID]
AND SY12.Grade = P.Grade
LEFT OUTER JOIN
v_Sites AS S ON SY12.SchoolID = S.SchoolID
GROUP BY SY12.[SchoolID], SY12.[Grade]
ORDER BY
CASE
WHEN SY12.Level= 'ES' THEN '1'
WHEN SY12.Level= 'MS' THEN '2'
WHEN SY12.Level= 'HS' THEN '3'
ELSE '4'
END
, SiteName
, CASE
WHEN SY12.Grade = 'K' THEN '1'
WHEN SY12.Grade = '1' THEN '2'
WHEN SY12.Grade = '2' THEN '3'
WHEN SY12.Grade = '3' THEN '4'
WHEN SY12.Grade = '4' THEN '5'
WHEN SY12.Grade = '5' THEN '6'
ELSE '7'
END
Looks like I have typos in the query. :(
SELECT
schoolid,
MAX(sitename),
MAX(level),
MAX(area),
grade,
SUM(20122013ActualEnrollment),
SUM(20132014ActualEnrollment),
SUM(20152016ActualEnrollment)
FROM supportContacts
GROUP BY schoolid, grade WITH ROLLUP
That worked in your fiddle. I know it is not ordered by but maybe you can run with it?

Calculating the sum of annual amounts when frequencies of payments differ

I'm currently trying to write a select statement which calculates the Annual sum in one single column.
The problem I am facing is that there are several different payment frequencies e.g. I would have to multiply a monthly amount by 12 to get the annual, quarterly by 4, semi annual by 2 etc.
I have written a statement below which does this, however it demands that I group by the frequency and amount fields, which gives the undesired result.
select (case when Frequency='month' then SUM(cast(Amount as decimal(10,2))*12)
else (case when Frequency='quarter' then SUM(cast(Amount as decimal(10,2))*4)
else (case when Frequency='year' then SUM(cast(Amount as decimal(10,2))*1)
else (case when Frequency='six months' then SUM(cast(Amount as decimal(10,2))*2) end)
end)
end) end) as 'Total Annual Amount'
from Table group by Frequency
I understand I maybe barking up the wrong tree as far as solving this problem, but the above is the closest I have gotten.
Hopefully I have been descriptive enough, if you need me to elaborate further please let me know
Try moving your sum outside of your case:
select
sum (case when Frequency='Month' then (cast(amount as decimal(10,2))*12
when Frequency='quarter' then...
end) as [Total Annual Amount]
You can also use WITH AS (assuming ORACLE)...
WITH dmap AS
( select 'Month' as duration, 12 as mult from dual
UNION
select 'Year' as duration, 1 as mult from dual
UNION
select 'Quater' as duration, 4 as mult from dual
UNION
select 'six months' as duration, 2 as mult from dual
)
select sum(cast(Amount as decimal(10,2)) * mult)
from tab, dmap
where duration = frequency;