I have this query. It should return Count for both AWARDED (1) and NOT AWARDED(0) works from works table.
Select Count(w.WorkID)as Total, w.IsAwarded, org.OrganizationName
From Works w
Inner Join MC_MemberShip.Membership.Organization org
ON org.OrganizationID= w.Organization_ID
where Convert(varchar(11), w.OpeningDate) >= Convert(varchar(11), #FromDate)
and Convert(varchar(11), w.OpeningDate) < DATEADD(day, 1, Convert(varchar(11), #ToDate))
and w.IsActive=1 and
ISNULL(w.IsAwarded,0)= 0 and w.Organization_ID= case when #OrgID= -1 then w.Organization_ID else #OrgID end
group by org.OrganizationName, w.IsAwarded
Now this query returns Total count for NOT AWARDED i.e. 0 only but i want to return count for AWARDED too in same query.
Organization TotalAwardedWorks TotalNotAwardedWorks
Town 1 1 2
Town 2 44 33
Your query should look something like this:
select org.OrganizationName,
Count(*) as Total,
sum(case when w.IsAwarded = 0 or w.IsAwarded is null then 1 else 0 end) as TotalNotAward,
sum(case when w.IsAwarded = 1 then 0 else 1 end) as TotalAward
from Works w Inner Join
MC_MemberShip.Membership.Organization org
on org.OrganizationID = w.Organization_ID
where w.OpeningDate >= #FromDate and
w.OpeningDate < dateadd(day, 1, #ToDate) and
w.IsActive = 1 and
(w.Organization_ID = #OrgId or #OrgID= -1)
group by org.OrganizationName;
Notes:
Do not convert dates to strings to perform comparisons. That is just perverse.
Generally, the use of case in the where clause is discouraged. The logic is more clearly represented using or.
You can get what you want by using case to put conditions in the aggregation functions.
Related
Newer SQL user working with SQL Server 2014 SP2.
I'm trying to determine how to improve my results on a particular query.
The problem is that when I select data with a Case When, it requires me to use group by to aggregate the data.
I am trying to eliminate this group by, and have not been able to determine a viable method for this query.
Query yields these results:
StoreID Devices playing devices FullScreenPlays PIPPlays
--------------------------------------------------------
1296 1 0 0 0
1296 7 7 7 0
1296 7 7 0 7
I am trying to achieve something more like this:
StoreID Devices playing devices FullScreenPlays PIPPlays
--------------------------------------------------------------
1296 8 7 7 7
I've tried several variants of calling the group by, and I've tried several variants of the case when, but I can't seem to figure out what I'm doing wrong...
Any insight would be appreciated!
SQL code is here:
DECLARE #Location varchar(50) = '1296'
SELECT
Location.ExternalCode As [StoreID],
COUNT(DISTINCT imdd.DeviceID) AS Devices,
COUNT(DISTINCT esr.DeviceID) AS [playing devices],
(CASE
WHEN (esr.EventID = 925)
THEN (COUNT(DISTINCT esr.DeviceID))
ELSE 0
END) AS [FullScreenPlays],
(CASE
WHEN (esr.EventID = 926)
THEN (COUNT(DISTINCT esr.DeviceID))
ELSE 0
END) AS [PIPPlays]
FROM
[iSenseMD].dbo.Location WITH (NOLOCK)
INNER JOIN
iSenseMD.dbo.LocationAttribute la WITH (NOLOCK) ON Location.LocationID = la.LocationID
AND la.AttributeID = 7
AND la.Value = 1
LEFT JOIN
[iSenseMD].[dbo].Device imdd WITH (NOLOCK) ON location.LocationID = imdd.LocationID
AND imdd.DeviceName NOT IN ('iX Gateway', 'A312778', 'A294874', '334873')
LEFT JOIN
[iSenseAnalytics].[dbo].[EventStringRollup] esr WITH (NOLOCK) ON imdd.LocationID = esr.LocationID
AND imdd.DeviceID = esr.DeviceID
AND esr.IntervalID = 1
AND esr.EventID IN (925, 926) --all plays
AND esr.RollupTimestamp >= dateadd(day, datediff(day, 1, GETDATE()), 0)
AND esr.RollupTimestamp < dateadd(day, datediff(day, 0, GETDATE()), 0)
WHERE
Location.IsActive = 1
AND Location.LocationName NOT LIKE '%duplicate%'
AND Location.ExternalCode = #Location --this to add the declaration above as site constraint
GROUP BY
Location.ExternalCode, esr.EventID
ORDER BY
iSenseMD.dbo.Location.ExternalCode
Any insight would be appreciated!
Thanks!
I would put the query in a CTE and then aggregate it from there
WITH CTE_Example
AS
( use iSenseMD
go
DECLARE #Location varchar(50) = '1296'
SELECT Location.ExternalCode As [StoreID]
,count(distinct imdd.DeviceID) as Devices
,count(distinct esr.DeviceID) as [playing devices]
,(case when (esr.EventID = 925) Then (count (distinct esr.DeviceID)) Else 0 End) As [FullScreenPlays]
,(case when (esr.EventID = 926) Then (count (distinct esr.DeviceID)) Else 0 End) As [PIPPlays]
FROM [iSenseMD].dbo.Location WITH (NOLOCK)
INNER JOIN iSenseMD.dbo.LocationAttribute la WITH (NOLOCK)
ON Location.LocationID = la.LocationID
AND la.AttributeID = 7
AND la.Value = 1
left JOIN [iSenseMD].[dbo].Device imdd WITH (NOLOCK) ON location.LocationID = imdd.LocationID
AND imdd.DeviceName NOT IN ('iX Gateway','A312778','A294874','334873')
left JOIN [iSenseAnalytics].[dbo].[EventStringRollup] esr WITH (NOLOCK) ON imdd.LocationID = esr.LocationID and imdd.DeviceID = esr.DeviceID
AND esr.IntervalID = 1
AND esr.EventID IN (925, 926) --all plays
AND esr.RollupTimestamp >= dateadd(day,datediff(day,1,GETDATE()),0)
AND esr.RollupTimestamp < dateadd(day,datediff(day,0,GETDATE()),0)
WHERE Location.IsActive = 1
AND Location.LocationName NOT LIKE '%duplicate%'
AND Location.ExternalCode = #Location --this to add the declaration above as site constraint
GROUP BY Location.ExternalCode
,esr.EventID)
--A GROUP BY might not be allowed either, I can't remember. But I think it'll be fine, let me know if there are any issues
/* ORDER BY
iSenseMD.dbo.Location.ExternalCode */
--I don't think an ORDER BY is allowed in a CTE so I have it commented out for now. Is it an absolute within the query?
SELECT StoreID, SUM(Devices), SUM([playing devices]), SUM(FullScreenPlays),
SUM(PIPPlays)
FROM CTE_Example
GROUP BY StoreID
Hope this helps and please let me know if there are any errors!
You need to place your case statement inside your count aggregate. This way you can remove the EventID in the group by
Example:
COUNT(DISTINCT
CASE WHEN (esr.EventID = 925) THEN esr.DeviceID
ELSE NULL
END) AS [FullScreenPlays]
Original:
(CASE
WHEN (esr.EventID = 925)
THEN (COUNT(DISTINCT esr.DeviceID))
ELSE 0
END) AS [FullScreenPlays]
I have a problem with my SQL Server query.
My query must display the total number of each category. Not the total amount of all categories.
SELECT [CATEGORY],
(SELECT COUNT(*)
FROM [Group_New_DB].[dbo].[INCIDENTSM1]
WHERE ([OPEN_TIME] >= #StartDate and [OPEN_TIME] < #EndDate + 1) ) AS OpenedCount,
(SELECT COUNT(*)
FROM [Group_New_DB].[dbo].[INCIDENTSM1]
WHERE ([CLOSE_TIME] >= #StartDate and [CLOSE_TIME] < #EndDate + 1)) AS ClosedCount
FROM [Group_New_DB].[dbo].[INCIDENTSM1]
GROUP BY CATEGORY
ORDER BY CATEGORY
The report consists of a table with 3 columns: Category, Registered, Closed.
1 column is the category name.
2 column is how many categories have been registered.
3 column -> how many columns were closed.
But result
The result that turned out as a result does not look right.
I don't quite understand what is the difference between total number vs total amount as the question doesn't provide any context.
Although, if you are trying to get OpenedCount and ClosedCount as you have named the columns, I suggest you try below:
SELECT
CATEGORY,
SUM(CASE WHEN (OPEN_TIME >= #start_date AND OPEN_TIME < #end_date+1)
THEN 1
ELSE 0
END) AS OPENED_COUNT,
SUM(CASE WHEN (CLOSED_TIME >= #start_date AND CLOSED_TIME < #end_date+1)
THEN 1
ELSE 0
END) AS CLOSED_COUNT
FROM
[Group_New_DB].[dbo].[INCIDENTSM1]
GROUP BY
CATEGORY
ORDER BY
CATEGORY
IMO, This is also a better way as it doesn't include multiple sub-queries.
Correction: changed COUNT to SUM as suggested by TriV - Thanks!
Try this instead -
SELECT [CATEGORY],
COUNT(CASE WHEN [OPEN_TIME] >= #StartDate and [OPEN_TIME] < #EndDate + 1 THEN 1 ELSE NULL END) AS OpenedCount,
COUNT(CASE WHEN [CLOSE_TIME] >= #StartDate and [CLOSE_TIME] < #EndDate + 1 THEN 1 ELSE NULL END) AS ClosedCount
FROM [Group_New_DB].[dbo].[INCIDENTSM1] GROUP BY
CATEGORY ORDER BY CATEGORY
I am trying to get data for last 2 month ...but the query does not give perfect result....
SELECT DAY(table_A.PaymentDate) as date1 ,
(case when MONTH(table_A.PaymentDate) = MONTH(CURRENT_TIMESTAMP) - 1
then CAST(SUM(table_A.Total_Amount) AS INT)
else 0
end) AS last_month_CNT,
(case when MONTH(table_A.PaymentDate) = MONTH(CURRENT_TIMESTAMP)
then CAST(SUM(table_A.Total_Amount) As INT)
else 0
end) as This_month_CNT
FROM Tbl_Pan_Paymentdetails table_A
FULL OUTER JOIN Tbl_Pan_Paymentdetails table_B
ON table_A.PaymentDate=table_B.PaymentDate
WHERE YEAR(table_A.PaymentDate) = YEAR(CURRENT_TIMESTAMP)
AND
table_A.PaymentDate >= DATEADD(MONTH, -1, GETDATE())
GROUP BY
DAY(table_A.PaymentDate) ,MONTH(table_A.PaymentDate)
order by
DAY(table_A.PaymentDate);
Move the entire case expression inside the sum function and don't include the month in the group by. Also, the full outer join seems unnecessary so I removed it.
This should be what you are looking for:
SELECT
DAY(PaymentDate) as date1 ,
SUM(CASE WHEN MONTH(PaymentDate) = MONTH(CURRENT_TIMESTAMP)-1 THEN CAST(Total_Amount AS INT) ELSE 0 END) AS last_month_CNT,
SUM(CASE WHEN MONTH(PaymentDate) = MONTH(CURRENT_TIMESTAMP) THEN CAST(Total_Amount AS INT) ELSE 0 END) AS This_month_CNT
FROM Tbl_Pan_Paymentdetails
WHERE YEAR(PaymentDate) = YEAR(CURRENT_TIMESTAMP)
AND PaymentDate >= DATEADD(MONTH, -1, GETDATE())
GROUP BY DAY(PaymentDate)
ORDER BY DAY(PaymentDate);
So I am trying to create a report which will give me a count of sales orders and compare them to a previous date ranges. unfortunately I am not sure how to approach returning the results as each of these calculations are ran against the same table column.
Ideally my output would look something like this, including the NULL values
partner Today LastYear TwoYear
------- ------ -------- --------
zzz 10 15 4
yyy 2 4
xxx 3 1 2
I have the basic idea down:
DECLARE #currentDay DATETIME
SET #currentDay = DATEDIFF(day,0,GETDATE()) -- Gives it 00:00:00.000 for time
-- Todays orders
SELECT count(s.po_id) as 'Orders Today',c.tp_name
FROM [EDI_001].[dbo].[303v850h] as s
join [EDI_001].[dbo].[Trade] as c
on s.TP_PartID = c.TP_PartID
where s.ExportDate < #currentDay AND
s.ExportDate > DATEADD(day,-1,#currentDay)
group by c.tp_name
order by c.tp_name;
-- Last Years Day's orders
SELECT count(s.po_id) as 'Orders Today',c.tp_name
FROM [EDI_001].[dbo].[303v850h] as s
join [EDI_001].[dbo].[Trade] as c
on s.TP_PartID = c.TP_PartID
where s.ExportDate < DATEADD(year,-1,#currentDay) AND
s.ExportDate > DATEADD(year, -1,DATEADD(day,-1,#currentDay))
group by c.tp_name
order by c.tp_name;
I'll go ahead and stop there, as you can see the queries are almost identical just changing the date range in the where clause. What I don't know is how to combine the two queries into a single result set. As well, my join does not return the empty sets in either query. I realize that it won't with the current join used, however it hasn't shown in different results with left outer joins either... But realistically one problem at a time and the first step is to get a single result set. Any help would be greatly appreciated.
DECLARE #currentDay DATETIME
SET #currentDay = DATEDIFF(day,0,GETDATE()) -- Gives it 00:00:00.000 for time
SELECT Sum(
CASE
WHEN s.ExportDate Between DATEADD(day,-1,#currentDay) AND #currentDay
THEN 1
ELSE 0
END
) As Today,
Sum(
CASE
WHEN s.ExportDate Between DATEADD(year, -1,DATEADD(day,-1,#currentDay)) AND DATEADD(year,-1,#currentDay)
THEN 1
ELSE 0
END
) As LastYear,
Sum(
CASE
WHEN s.ExportDate Between DATEADD(year, -2,DATEADD(day,-1,#currentDay)) AND DATEADD(year,-2,#currentDay)
THEN 1
ELSE 0
END
) As TwoYear,
c.tp_name
FROM [EDI_001].[dbo].[303v850h] as s
JOIN [EDI_001].[dbo].[Trade] as c
on s.TP_PartID = c.TP_PartID
GROUP BY c.tp_name
ORDER BY c.tp_name;
You are looking for the UNION operator.
It's used to combine the result-set of two or more SELECT statements.
http://www.w3schools.com/sql/sql_union.asp
You can use a conditional aggregate:
SELECT c.tp_name,
Today = COUNT(CASE WHEN s.ExportDate > DATEADD(DAY,-1,#currentDay) THEN s.po_id END),
LastYear = COUNT(CASE WHEN s.ExportDate > DATEADD(YEAR,-1,#currentDay)
AND s.ExportDate < DATEADD(YEAR, -1,DATEADD(DAY, -1, #currentDay))THEN s.po_id END),
TwoYear = COUNT(CASE WHEN s.ExportDate > DATEADD(YEAR,-2, #currentDay)
AND s.ExportDate < DATEADD(YEAR, -2, DATEADD(DAY, -1, #currentDay))THEN s.po_id END),
FROM [EDI_001].[dbo].[303v850h] as s
JOIN [EDI_001].[dbo].[Trade] as c
ON s.TP_PartID = c.TP_PartID
WHERE s.ExportDate < #currentDay AND
s.ExportDate > DATEADD(YEAR, -2, DATEADD(DAY, -1, #currentDay))
GROUP BY c.tp_name
ORDER BY c.tp_name;
So you are essentially moving each of your WHERE clauses to a CASE statement inside the the COUNT, so you will only count records where your criteria is met.
I have this query where I get totals of different stats from an employee roster table.
SELECT A.rempid AS EmpId,
E.flname,
A.rdo_total,
B.grave_total,
C.sundays,
D.holidays
FROM (SELECT rempid,
Count(rshiftid)AS RDO_Total
FROM rtmp1
WHERE rshiftid = 2
GROUP BY rempid
HAVING Count(rshiftid) > 0) A,
(SELECT rempid,
Count(rshiftid)AS Grave_Total
FROM rtmp1
WHERE rshiftid = 6
GROUP BY rempid
HAVING Count(rshiftid) > 0)B,
(SELECT rempid,
Count(rshiftid) AS Sundays
FROM rtmp1
WHERE Datepart(dw, rdate) = 1
AND rshiftid > 2
GROUP BY rempid
HAVING Count(rshiftid) > 0)C,
(SELECT rempid,
Count(rshiftid) AS Holidays
FROM rtmp1
WHERE rdate IN (SELECT pubhdt
FROM pubhol)
AND rshiftid > 2
GROUP BY rempid
HAVING Count(rshiftid) > 0)D,
(SELECT empid,
[fname] + ' ' + [sname] AS flName
FROM remp1)E
WHERE A.rempid = B.rempid
AND A.rempid = E.empid
AND A.rempid = C.rempid
AND A.rempid = D.rempid
ORDER BY A.rempid
I would like to add a date range into it, so that I can query the database within 2 dates. The rTmp1 table has a column called rDate. I was wondering what the best way to do this. I could add it to a stored procedure and add variable to each select query. Or is there a better way to run the query within a date range.
i think just add an additional where clause item similar to:
AND ( rDate > somedate AND rDate < someotherdate )
Adding the date range to each query is the most direct solution.
Making it a stored procedure is something that can always be done with a query, but has nothing to do with this specific case.
If the number of records resulting from narrowing down your table to the specified date range is substantially less than the entire table, it might be an option to insert these records into a temporary table or a table variable and run your existing query on that table/resultset.
Though I do not have any data to test, you might consider the following query as it is more easy to read and might perform better. But you have to check the results for yourself and maybe do some adjustments.
DECLARE #startDate date = '12/01/2012'
DECLARE #endDate date = DATEADD(MONTH, 1, #startDate)
SELECT
[e].[empid],
[e].[fname] + ' ' + [e].[sname] AS [flName],
SUM(CASE WHEN [t].[rshiftid] = 2 THEN 1 ELSE 0 END) AS [RDO_Total],
SUM(CASE WHEN [t].[rshiftid] = 6 THEN 1 ELSE 0 END) AS [Grave_Total],
SUM(CASE WHEN [t].[rshiftid] > 2 AND DATEPART(dw, [t].[rdate]) = 1 THEN 1 ELSE 0 END) AS [Sundays],
SUM(CASE WHEN [t].[rshiftid] > 2 AND [h].[pubhdt] IS NOT NULL THEN 1 ELSE 0 END) AS [Holidays]
FROM [remp1] [e]
INNER JOIN [rtmp1] [t] ON [e].[empid] = [t].[rempid]
LEFT JOIN [pubhol] [h] ON [t].[rdate] = [h].[pubhdt]
WHERE [t].[rdate] BETWEEN #startDate AND #endDate
GROUP BY
[e].[empid],
[e].[fname],
[e].[sname]
ORDER BY [empid] ASC