Can I add multiple summary rows in TSQL? - sql

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?

Related

Oracle SQL group by rollup and ratio_to_report

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.

PIVOT from JOIN tables

I have a large database and in this database, there are two tables I need to pull information from. I have pulled all the data I need out of the two tables by using both a JOIN and a CASE WHEN. Here is a screen shot from the output
SQL Server output
This is the code that I used to pull the data:
SELECT [PORTMultiMax].[dbo].cardholdertable.cardid as CardID,CardHolderTable.FirstName as FirstName,CardHolderTable.LastName as LastName, CardHolderTable.InitLet as MI, CardHolderPersonalDataXrTable.PersonalDataItem as Data,
CASE WHEN PersonalDataID = '4' THEN 'SSN'
WHEN PersonalDataID = '22' THEN 'Employer'
WHEN PersonalDataID = '30' THEN 'Training Type'
WHEN PersonalDataID = '32' THEN 'Primary Sponsor'
WHEN PersonalDataID = '37' THEN 'Training Date'
ELSE NULL END AS Description
FROM [PORTMultiMax].[dbo].[CardHolderTable]
join [PORTMultiMax].[dbo].[CardHolderPersonalDataXrTable]
on cardholdertable.CardID=CardHolderPersonalDataXrTable.CardID
where PersonalDataID IN (4,22,30,32,33,37)
order by LastName
The tables involved are named: CardHolderTable and CardHolderPersonalDataXrTable
What I need to do next is get rid of the duplicate name entries in the data. So for example, "JAMES AARON" has multiple rows due to him having multiple descriptors ("Training Date, TrainingType, Employer, and SSN").
I wanted to try and use a PIVOT to pull the row data out and put them in columns named "SSN, Employer, etc...". My problem is I have never used PIVOT before and I am confused on how to apply a PIVOT code to my current SQL query.
PLEASE HELP. Thank you so much
Given your query, I think conditional aggregation is simpler:
SELECT ch.FirstName, ch.LastName
MAX(CASE WHEN PersonalDataID = '4' THEN 1 ELSE 0 END) as is_SSN
MAX(CASE WHEN PersonalDataID = '22' THEN ELSE 0 END) as is_Employer,
MAX(CASE WHEN PersonalDataID = '30' THEN ELSE 0 END) as is_TrainingType,
MAX(CASE WHEN PersonalDataID = '32' THEN ELSE 0 END) as is_PrimarySponsor,
MAX(CASE WHEN PersonalDataID = '37' THEN ELSE 0 END) as is_TrainingDate
from [PORTMultiMax].[dbo].[CardHolderTable] ch join
[PORTMultiMax].[dbo].[CardHolderPersonalDataXrTable] cp
on ch.CardID = cp.CardID
where PersonalDataID IN (4, 22, 30, 32, 33, 37)
group by ch.FirstName, ch.LastName;

Limit SQL query to days

I use this SQL query to make status report by day:
CREATE TABLE TICKET(
ID INTEGER NOT NULL,
TITLE TEXT,
STATUS INTEGER,
LAST_UPDATED DATE,
CREATED DATE
)
;
Query:
SELECT t.created,
COUNT(CASE WHEN t.status = '1' THEN 1 END) as cnt_status1,
COUNT(CASE WHEN t.status = '2' THEN 1 END) as cnt_status2,
COUNT(CASE WHEN t.status = '3' THEN 1 END) as cnt_status3,
COUNT(CASE WHEN t.status = '4' THEN 1 END) as cnt_status4
FROM ticket t
GROUP BY t.created
How I can limit this query to last 7 days?
Also I would like to get the results split by day. Fow example I would like to group the first dates for 24 hours, second for next 24 hours and etc.
Expected result:
This might help:
SELECT TO_CHAR(t.created, 'YYYY-MM-DD') AS created_date,
COUNT(CASE WHEN t.status = '1' THEN 1 END) as cnt_status1,
COUNT(CASE WHEN t.status = '2' THEN 1 END) as cnt_status2,
COUNT(CASE WHEN t.status = '3' THEN 1 END) as cnt_status3,
COUNT(CASE WHEN t.status = '4' THEN 1 END) as cnt_status4
FROM ticket t
WHERE t.created >= SYSDATE-7
GROUP BY TO_CHAR(t.created, 'YYYY-MM-DD')
ORDER BY created_date;
I used the oracle function for date conversion. I'm sure you'll find the corresponding one for postgresql.

SQL: Trying to understand IF/ELSE

SELECT CASE r.SourceId
WHEN '1' THEN 'ITUNES'
WHEN '2' THEN 'SFR'
WHEN '3' THEN 'ORANGE'
ELSE 'Others'
END as source
, CAST(SUM (r.SalesVolume) AS DECIMAL(14, 4) ) AS Volume
, CAST(SUM (r.SalesVolume * r.CustomerPrice) AS DECIMAL(14, 4) ) AS Value
from Rawdata r
INNER JOIN Product p
ON p.ProductId = r.ProductId
INNER JOIN Calendar c
ON r.DayId = c.DayId
WHERE c.WeekId BETWEEN (20145227) AND (20155230)
AND p.ContentFlavor IN ('SD', 'HD')
AND p.VODEST IN ('VOD','EST')
AND p.Distributor IN ('M6SND')
GROUP BY CASE r.SourceId
WHEN '1' THEN 'ITUNES'
WHEN '2' THEN 'SFR'
WHEN '3' THEN 'ORANGE'
ELSE 'Others'
END
The result of the above query is:
source Volume Value
ITUNES 48316.0000 506067.2600
This result is perfectly OK since my source table RawData doesnt contain any values for SourceId 2 or 3.
But what I basically want is the result to look like is:
source Volume Value
ITUNES 48316.0000 506067.2600
SFR 0 0
ORANGE 0 0
Others 0 0
If there is no value corresponding to any column parameter then I need it to be 0
I assume this could be done using IF/ELSE but not sure how?
with the help of a CTE this is a way to do it. (replace the first query with something more dynamic if you want)
with myChoices (choices)
as (
select
choices
from (
values
('ITUNES'),
('SFR'),
('ORANGE'),
('Others')
) [ ] (choices)
),
myQuery ([source],[Volume],[Value])
as (
SELECT CASE r.SourceId
WHEN '1' THEN 'ITUNES'
WHEN '2' THEN 'SFR'
WHEN '3' THEN 'ORANGE'
ELSE 'Others'
END as source
, CAST(SUM (r.SalesVolume) AS DECIMAL(14, 4) ) AS Volume
, CAST(SUM (r.SalesVolume * r.CustomerPrice) AS DECIMAL(14, 4) ) AS Value
from Rawdata r
INNER JOIN Product p
ON p.ProductId = r.ProductId
INNER JOIN Calendar c
ON r.DayId = c.DayId
WHERE c.WeekId BETWEEN (20145227) AND (20155230)
AND p.ContentFlavor IN ('SD', 'HD')
AND p.VODEST IN ('VOD','EST')
AND p.Distributor IN ('M6SND')
GROUP BY CASE r.SourceId
WHEN '1' THEN 'ITUNES'
WHEN '2' THEN 'SFR'
WHEN '3' THEN 'ORANGE'
ELSE 'Others'
END
)
select
c.choices,
ISNULL(q.Volume,0)Volume,
ISNULL(q.Value,0)Value
from myChoices c
left join myQuery q on
c.choices = q.[source]
Create an inline view called "Product_Inline_View", which is like
(select 1 as SourceId, 'ITUNES' as source_name
union all
select 2 as SourceId, 'SFR' as source_name
union all
select 3 as SourceId, 'ORANGE' as source_name
)
Right Join the Product_Inline_view with the Query you have, but without the CASE.
And then do the group by.

SQL CASE Logic Issue

Could someone please assist me? Example: Problem I am having is when an account has AV.Query='PN4', AV.Response = 'Y', AV.Query='FL1' and AV.Response='Y', the result needs to be
PStatus IStatus
4 2
However, I am getting
PStatus IStatus
4 5
5 2
It picks "5" all the time on the opposite column.
SELECT distinct A.AcctNum,
CASE
WHEN O.Order = 'NEI2' THEN '1'
WHEN AV.Query IN ('PNE1','PNE2') AND AV.Response = 'Y' THEN '2'
WHEN AV.Query = 'PN20' AND AV.Response = 'Y' THEN '3'
WHEN AV.Query = 'PN4' AND AV.Response = 'Y' THEN '4'
ELSE '5'
END AS [PStatus],
CASE
WHEN O.Order IN ('DO2','FL25','VACHP') THEN '1'
WHEN AV.Query = 'FL1' AND AV.Response = 'Y' THEN '2'
WHEN AV.Query = 'REF' AND AV.Response = 'Y' THEN '3'
WHEN AV.Query IN ('FL2','FL6','NEU.G','HE.B') AND AV.Response = 'Y' THEN '4'
WHEN AV.Query = 'NOA' AND AV.Response = 'Y' THEN '6'
ELSE '5'
END AS [IStatus]
FROM AData AS AD
INNER JOIN AVisit AS AV
ON AD.Visit = AV.Visit
AND AV.QueryID IN ('PNE1','PNE2','PN20','PN4','FL1','REF','FL2','FL6','NEU.G','HE.B','NOA')
LEFT JOIN Order AS O
ON AD.Visit = O.Visit
AND O.Order IN ('NEI2','DO2','FL25','VACHP');
Your results appear to be correct for the query.
Consider first the row of the join that has
AV.Query='PN4', AV.Response = 'Y',
This matches the last WHEN condition in the CASE expression for [PStatus], but it does not match any of the WHEN conditions in the CASE expression for [IStatus]. Result:
4 5
Consider now the join row having
AV.Query='FL1' and AV.Response='Y'
This does not match any WHEN condition in the CASE expression for [PStatus], but it matches the second WHEN conditions in the CASE expression for [IStatus]. Result:
5 2
I could give you a variation on your query that produces the results you specified for the particular data you present, but I do not do so because there are many alternatives, and you haven't given us the information to determine which one correctly answers the question for other data.
UPDATE:
To combine multiple result rows into one, you require an aggregate query. One possible way to implement such a query to provide a single row having values 4, 2 for your given data would be
SELECT
AcctNum,
MIN(
CASE
WHEN O.Order = 'NEI2' THEN '1'
WHEN AV.Query IN ('PNE1','PNE2') AND AV.Response = 'Y' THEN '2'
WHEN AV.Query = 'PN20' AND AV.Response = 'Y' THEN '3'
WHEN AV.Query = 'PN4' AND AV.Response = 'Y' THEN '4'
ELSE NULL
END
) AS [Pstatus],
MIN(
CASE
WHEN O.Order IN ('DO2','FL25','VACHP') THEN '1'
WHEN AV.Query = 'FL1' AND AV.Response = 'Y' THEN '2'
WHEN AV.Query = 'REF' AND AV.Response = 'Y' THEN '3'
WHEN AV.Query IN ('FL2','FL6','NEU.G','HE.B') AND AV.Response = 'Y' THEN '4'
WHEN AV.Query = 'NOA' AND AV.Response = 'Y' THEN '6'
ELSE NULL
END
) AS [IStatus]
FROM
AData AS AD
INNER JOIN AVisit AS AV
ON AD.Visit = AV.Visit
AND AV.QueryID IN ('PNE1','PNE2','PN20','PN4','FL1','REF','FL2','FL6','NEU.G','HE.B','NOA')
LEFT JOIN Order AS O
ON AD.Visit = O.Visit
AND O.Order IN ('NEI2','DO2','FL25','VACHP');
GROUP BY AcctNum
This works because aggregate functions other than COUNT() ignore NULLs. It very likely will not give the desired answer for different data patterns, however -- especially patterns where there are more than two AVisits for the same account.
Results are correct.
Your values are:
First row - AV.Query='PN4', AV.Response = 'Y'
Second row - AV.Query='FL1', AV.Response='Y'
First CASE PStatus: Result is 4 as you expected.
Next Case, Istatus. Here is probably lying misunderstanding, Because SQL will process still the same values (First row). This CASE will check against values: AV.Query='PN4', AV.Response = 'Y' from first line. Not against your second values (AV.Query='FL1' and AV.Response='Y').
Values 'PN4' and 'FL1' can not be in same Column in a same line! One column can have only one value, not two. You are probably expecting that in this second case SQL will start fetching another (second) line of data. But it is not.
Second case result is correct Because in the CASE there is not condition for value PN4, so it is end up as ELSE = 5
OK Edit, do it like this, this brute force, but will work ;-)
SELECT A.AcctNum,
COALESCE(MAX( CASE
WHEN O.Order = 'NEI2' THEN '1'
WHEN AV.Query IN ('PNE1','PNE2') AND AV.Response = 'Y' THEN '2'
WHEN AV.Query = 'PN20' AND AV.Response = 'Y' THEN '3'
WHEN AV.Query = 'PN4' AND AV.Response = 'Y' THEN '4'
ELSE NULL
END),5) AS [PStatus],
COALESCE(MAX(CASE
WHEN O.Order IN ('DO2','FL25','VACHP') THEN '1'
WHEN AV.Query = 'FL1' AND AV.Response = 'Y' THEN '2'
WHEN AV.Query = 'REF' AND AV.Response = 'Y' THEN '3'
WHEN AV.Query IN ('FL2','FL6','NEU.G','HE.B') AND AV.Response = 'Y' THEN '4'
WHEN AV.Query = 'NOA' AND AV.Response = 'Y' THEN '6'
ELSE NULL
END),5) AS [IStatus]