Combining groups in GROUP BY - sql

I have a query that groups by the field description. (There are 9 total descriptions.) I need to somehow combine 3 of these descriptions into one group, giving me a total of 7 descriptions. I'm brand new to sql querying, so I'm fairly lost on anything beyond JOINS and WHERE clauses so far. I appreciate the help.
SELECT serviceclass.description, DATEPART(Year, meterread.readdate) AS Year, DATEPART(Month, meterread.readdate) AS Month, SUM(meterread.consumption)
AS Consumption, COUNT(DISTINCT accountservice.account_id) AS Locations
FROM accountservice INNER JOIN
serviceclass ON accountservice.serviceclass_id = serviceclass.serviceclass_id INNER JOIN
meterread ON accountservice.accountservice_id = meterread.accountservice_id
WHERE (#Year = DATEPART(Year, meterread.readdate))
GROUP BY serviceclass.description, DATEPART(Year, meterread.readdate), DATEPART(Month,meterread.readdate)
ORDER BY Year, Month

You can achieve this by using a CASE statement.
replace serviceclass.desription in your SELECT and GROUP BY clauses with
CASE
WHEN serviceclass.description IN ('Desc1','Desc2','Desc3')
THEN 'JointDescription'
ELSE serviceclass.description
END
and also update the Desc 1 ~ 3 with your three description and updated the joint description to what you want it to be.
RESULT:
SELECT CASE
WHEN serviceclass.description IN ('Desc1','Desc2','Desc3')
THEN 'JointDescription'
ELSE serviceclass.description
END as description,
DATEPART(Year, meterread.readdate) AS Year,
DATEPART(Month, meterread.readdate) AS Month,
SUM(meterread.consumption) AS Consumption,
COUNT(DISTINCT accountservice.account_id) AS Locations
FROM accountservice
INNER JOIN serviceclass ON accountservice.serviceclass_id = serviceclass.serviceclass_id
INNER JOIN meterread ON accountservice.accountservice_id = meterread.accountservice_id
WHERE (#Year = DATEPART(Year, meterread.readdate))
GROUP BY CASE
WHEN serviceclass.description IN ('Desc1','Desc2','Desc3')
THEN 'JointDescription'
ELSE serviceclass.description
END,
DATEPART(Year, meterread.readdate),
DATEPART(Month,meterread.readdate)
ORDER BY Year, Month

You would typically do this with a case statement that would go in both the select and group by statements. Something like:
select (case when serviceclass.description = 'A' then 'B'
else serviceclass.description
end) as description
. . . -- fill in with query here
group by (case when serviceclass.description = 'A' then 'B'
else serviceclass.description
end), . . . -- rest of query goes here

Related

union of two queries in one

I have two queries with different result sets but only one of the conditions is additional in one of the queries. Both queries are fetching from the same table.
first query:
SELECT cm.ctm, count_big(*) AS TOTAL
FROM dbo_cm.cm
WHERE
cm.a= 'abc' AND
cm.b= 1 AND
cm.ps IS NOT NULL AND
datepart(MONTH, cm.ps) = 7 AND
datepart(YEAR, cm.ps) = 2015
GROUP BY cm.ctm
second query:
SELECT cm.ctm, count_big(*) AS TOTAL
FROM dbo_cm.cm
WHERE
cm.a= 'abc' AND
cm.b= 1 AND
cm.ps IS NOT NULL AND
datepart(MONTH, cm.ps) = 7 AND
datepart(YEAR, cm.ps) = 2015 and
cm.as>cm.ps
GROUP BY cm.ctm
How do I make this query simpler by merging it into one? We use decode in Oracle for this purpose.
You can merge these two queries into one by moving the additional condition in the second query to a case expression and returning null when the condition is not met. count_big, like any aggregate function, will just ignore the nulls:
SELECT cm.ctm,
COUNT_BIG(*) AS total1,
COUNT_BIG(CASE WHEN cm.as > cm.ps THEN 1 ELSE NULL END) AS total2
FROM dbo_cm.cm
WHERE cm.a= 'abc' AND
cm.b= 1 AND
cm.ps IS NOT NULL AND
DATEPART(MONTH, cm.ps) = 7 AND
DATEPART(YEAR, cm.ps) = 2015 AND
GROUP BY cm.ctm
You can use SUM(CASE...) like:
SELECT
cm.ctm,
count_big(*) AS TOTAL1,
SUM(CASE WHEN cm.[as]>cm.[ps] THEN 1 ELSE 0 END) AS TOTAL2
FROM dbo_cm.cm
WHERE cm.a= 'abc'
AND cm.b= 1
AND cm.ps IS NOT NULL
AND datepart(MONTH, cm.ps) = 7
AND datepart(YEAR, cm.ps) = 2015
GROUP BY cm.ctm

sorting month in ascending order

I have looked at related Qs on Stack.
I am trying to sort my query to display month in ascending order. A similar post on Stack said that I must indicate "ASC", but this doesn't work for me. I have written an Order By. I cannot figure out why it is not working.
SELECT DATENAME( MONTH,(Submission.SubmissionDate)) AS [Date]
FROM Submission
GROUP BY DATENAME( MONTH,(Submission.SubmissionDate)) ORDER BY [Date] ASC;
This is the output:
Month
August
February
September
Note: this is just a part of my query, I didnt think it would necessary to show the rest, which relates to other attributes from my table (Not month related)
Edit: This is my Entire Query: (The Initial One)
SELECT DATENAME( MONTH,(Submission.SubmissionDate)) AS [Date],
SUM( CASE WHEN Submission.Status='Under review' THEN 1 ELSE 0 END) [Under_Review],
SUM( CASE WHEN Submission.Status='Accepted' THEN 1 ELSE 0 END) [Accepted],
SUM( CASE WHEN Submission.Status='Rejected' THEN 1 ELSE 0 END) [Rejected],
SUM( CASE WHEN Submission.Status='In print' THEN 1 ELSE 0 END) [In_print],
SUM( CASE WHEN Submission.Status='Published' THEN 1 ELSE 0 END) [Published]
FROM Submission INNER JOIN ((Faculty INNER JOIN School ON Faculty.FacultyID = School.[FacultyID]) INNER JOIN (Researcher INNER JOIN ResearcherSubmission ON Researcher.ResearcherID = ResearcherSubmission.ResearcherID) ON School.SchoolID = Researcher.SchoolID) ON Submission.SubmissionID = ResearcherSubmission.SubmissionID
GROUP BY DATENAME( MONTH,(Submission.SubmissionDate))
ORDER BY DATENAME( MONTH,(Submission.SubmissionDate));
SELECT DATENAME( MONTH, Submission.SubmissionDate) AS [Date]
FROM Submission
ORDER BY datepart(mm,Submission.SubmissionDate)
You don't need a group by (for the query shown). Also, when you order by month name it would return results in the alphabetical order of month name. You should not use previously defined aliases in the where,order by having and group by clauses.
Edit: The problem is with the join conditions. You should correct them as per the comments in line.
SELECT DATENAME( MONTH,(Submission.SubmissionDate)) AS [Date],
SUM( CASE WHEN Submission.Status='Under review' THEN 1 ELSE 0 END) [Under_Review],
SUM( CASE WHEN Submission.Status='Accepted' THEN 1 ELSE 0 END) [Accepted],
SUM( CASE WHEN Submission.Status='Rejected' THEN 1 ELSE 0 END) [Rejected],
SUM( CASE WHEN Submission.Status='In print' THEN 1 ELSE 0 END) [In_print],
SUM( CASE WHEN Submission.Status='Published' THEN 1 ELSE 0 END) [Published]
FROM Faculty
INNER JOIN School ON Faculty.FacultyID = School.[FacultyID]
INNER JOIN Researcher ON School.SchoolID = Researcher.SchoolID
INNER JOIN ResearcherSubmission ON Researcher.ResearcherID = ResearcherSubmission.ResearcherID
INNER JOIN SUBMISSION ON Submission.SubmissionID = ResearcherSubmission.SubmissionID
GROUP BY DATENAME( MONTH,(Submission.SubmissionDate))
ORDER BY DATEPART( MONTH,(Submission.SubmissionDate))
A simple option is to add MONTH(Submission.SubmissionDate) to your group by clause, and order by that as well:
SELECT DATENAME( MONTH,(Submission.SubmissionDate)) AS [Date]
FROM Submission
GROUP BY MONTH(Submission.SubmissionDate), DATENAME( MONTH,(Submission.SubmissionDate))
ORDER BY MONTH(Submission.SubmissionDate)
This will work for your real query as well.
You can use MIN() or MAX():
SELECT DATENAME(MONTH,(Submission.SubmissionDate)) AS [Date]
FROM Submission
GROUP BY DATENAME(MONTH,(Submission.SubmissionDate))
ORDER BY MIN([Date]) ASC;
This chooses an arbitrary date from each group and orders by that.
By the way, you probably should care about the year as well as the month. If so:
SELECT YEAR(s.SubmissionDate), DATENAME(MONTH, s.SubmissionDate) AS [Date]
FROM Submission s
GROUP BY YEAR(s.SubmissionDate), DATENAME(MONTH, s.SubmissionDate)
ORDER BY MIN([Date]) ASC;

SQL - Inserting a condition in a GROUP BY

My issue is that some of the records in the result set are excluded because they are missing a Min_Date or Max_Date or Both. I need these records to be included so that I can show the runner ran in the race even if he did not reach a First, Last or any checkpoint. Any direction is appreciated.
SELECT A.Date, A.RunnerName, A.Duplicates, A.TotalWaypointsReached,
B.FirstWaypoint, C.LastWaypoint, C.rDateTime as MostRecent
FROM (
SELECT RunnerName,
CONVERT(NVARCHAR(25), rDatetime, 101) AS Date,
Min(case when FirstWaypoint is null OR FirstWaypoint = '' then null else rDateTime end) MIN_DATE,
Max(case when LastWaypoint is null OR LastWaypoint = '' then null else rDateTime end) MAX_DATE,
--IF(I'm missing a Max_Date, Min_Date, or both after all records in a group. Add Max(rDateTime) and Min(rDateTime))
Count(*) AS Duplicates,
SUM(TotalWaypoints) as TotalWaypointsReached
FROM Race A
GROUP BY RunnerName, CONVERT(NVARCHAR(25), rDateTime, 101)
HAVING Count(*) > 1 ) A
LEFT JOIN Race B
on A.RunnerName = B.RunnerName
and A.MIN_DATE = B.rDateTime
LEFT JOIN Race C
on A.RunnerName = C.RunnerName
and A.MAX_DATE = C.rDatetime
I'm using the select statement via SQL command in Visual Studio 2008.

SQL Sum MTD & YTD

I have only just started looking into SQL.
I have a SQL Server 2008 r2 database that will return two fields DocDate & InvValue. I need to sum the InvValues as MTD & YTD as of Today's Date So it looks like
**Period** /////// **Total value**
MTD ////////////111111.11
YTD /////////////999999.99
I have done a fair amount of Googling and can do one or the other with SUM & DATEPART, but I am stuck with trying to do both.
Can someone give me some pseudo-code that would help me google a little further.
Thank you #Gordon Linoff, That helped a lot and I learned something, That I will find useful in the future.
My code now looks like:
SELECT
SUM(CASE WHEN YEAR(T1.[DocDate]) = YEAR(GETDATE()) THEN T0.[TotalSumSy] END) AS YTD,
SUM(CASE WHEN YEAR(T1.[DocDate]) = YEAR(GETDATE()) AND MONTH(T1.[DocDate]) = MONTH(GETDATE()) THEN T0.[TotalSumSy] END) AS MTD
FROM [dbo].[INV1] T0 INNER JOIN [dbo].[OINV] T1 ON T1.[DocEntry] = T0.[DocEntry]
However I now get
YTD.........MTD
99999.99....111111.11
And I need
YTD........99999.99
MTD........11111.11
Any further assistance would be appreciated.
You can do this with conditional aggregation:
select sum(case when year(docdate) = year(getdate()) then InvValue end) as YTD,
sum(case when year(docdate) = year(getdate()) and month(docdate) = month(getdaate())
then InvValue
end) as MTD
from table t;
This assumes you have no future dates in the table. If you do, add in docdate < getdate() to both clauses.
EDIT:
If you need this in two rows, you can simply do this:
select (case when n.n = 1 then 'YTD' else 'MTD' end) as which,
(case when n.n = 1 then YTD else MTD end) as value
from (select sum(case when year(docdate) = year(getdate()) then InvValue end) as YTD,
sum(case when year(docdate) = year(getdate()) and month(docdate) = month(getdaate())
then InvValue
end) as MTD
from table t
) cross join
(select 1 as n union all select 2) n;
SELECT
Period = 'MTD',
Total_value = SUM(T0.TotalSumSy)
FROM dbo.INV1 T0
INNER JOIN dbo.OINV T1
ON T1.DocEntry = T0.DocEntry
WHERE
T1.DocDate >= DATEADD(month,DATEDIFF(month,'20010101',GETDATE()),'20010101')
AND
T1.DocDate < DATEADD(month,1+DATEDIFF(month,'20010101',GETDATE()),'20010101')
UNION ALL
SELECT
'YTD',
SUM(T0.TotalSumSy)
FROM dbo.INV1 T0
INNER JOIN dbo.OINV T1
ON T1.DocEntry = T0.DocEntry
WHERE
T1.DocDate >= DATEADD(year,DATEDIFF(year,'20010101',GETDATE()),'20010101')
AND
T1.DocDate < DATEADD(year,1+DATEDIFF(year,'20010101',GETDATE()),'20010101') ;
The (complicated) conditions at the WHERE clauses are used instead of the YEAR(column) = YEAR(GETDATE() and the other you had previously, so indexes can be used. WHen you apply a function to a column, you make indexes unsuable (with some minor exceptions for some functions and some verios of SQL-Server.) So, the best thing is to try to convert the conditions to this type:
column <operator> AnyComplexFunction()

SQL Query to compare 2 weeks

I've got to design a query in visual studio where I have 2 data sets.
basically it goes like this.
I want to compare this weeks call total to last week per country calling.
the only thing is last weeks calls may have come from 20 diff countries while this weeks might only have come from 15.
How can I make the query such that the 20 countries will show up for both while having "0" value in for countries that do not appear this week.
below is my query:
Select country,
Sum(Case When actstatus in (5,105) Then 1 Else 0 End) As TotalCalls,
Sum(Case When actstatus = 105 Then 1 Else 0 End) As FailedCalls
From termactivity(nolock)
INNER JOIN termconfig(NOLOCK) ON cfgterminalID = actterminalID
INNER JOIN Country (nolock) on country = cycode
Where actstatus in (5,105)
and (actTerminalDateTime BETWEEN #StartDate-7 AND #EndDate-7)
Group By country
order By country asc
When Act status = 105 it means the call was not completed and when it = 5 it means the call was successful. I am doing this to get a successful call % rate per week.
Thanks in Advance!
Apply the same logic as you did to total calls and failed calls as you did to the this week and last week.
SELECT country,
COUNT(CASE WHEN actTerminalDateTime < #StartDate THEN 1 END) [LastWeekTotalCalls],
COUNT(CASE WHEN ActStatus = 105 AND actTerminalDateTime < #StartDate THEN 1 END) [LastWeekFailedCalls],
COUNT(CASE WHEN actTerminalDateTime >= #StartDate THEN 1 END) [ThisWeekTotalCalls],
COUNT(CASE WHEN ActStatus = 105 AND actTerminalDateTime >= #StartDate THEN 1 END) [ThisWeekFailedCalls]
FROM termactivity (NOLOCK)
INNER JOIN termconfig (NOLOCK)
ON cfgterminalID = actterminalID
INNER JOIN Country (NOLOCK)
ON country = cycode
WHERE actstatus in (5,105)
AND actTerminalDateTime BETWEEN DATEADD(DAY, -7, #StartDate) AND #EndDate
GROUP BY country
ORDER BY country ASC
I've also tidied up your query slightly, for example there is no point in specifying
WHEN ActStatus IN (5, 105) ...
When your WHERE clause already limits all results to 5, 105, therefore this is a redundant predicate in your case expression
From what I understand, you want to perform separate queries for two weeks, and you want both queries to produce rows for all countries, regardless of whether all countries had any calls. To achieve this, you need to use LEFT OUTER JOINS. The below code should guarantee that every country found in the Country table has a row, even if both sums are 0.
SELECT country,
SUM(CASE WHEN actstatus IN (5,105) THEN 1 ELSE 0 END) AS TotalCalls,
SUM(CASE WHEN actstatus = 105 THEN 1 ELSE 0 END) AS FailedCalls
FROM Country (NOLOCK)
LEFT OUTER JOIN termconfig (NOLOCK) ON country = cycode
LEFT OUTER JOIN termactivity (NOLOCK) ON cfgterminalID = actterminalID
WHERE (actTerminalDateTime BETWEEN #StartDate-7 AND #EndDate-7)
GROUP BY country
ORDER BY country ASC
If this was not what you wanted, perhaps you need to clarify your question. Many others have assumed that you want to combine the results into a single query.