Select Distinct Case Query - sql

I have the below query
SELECT Distinct dbo.WSUS_ComputerUpdateStatusWithApprovals.fulldomainname AS [Computer Name]
,dbo.WSUS_ComputerUpdateStatusWithApprovals.title AS [Update Title]
,count((case when (DATEDIFF(day, [ArrivalDate], GetDate())) > '0' and (DATEDIFF(day, [ArrivalDate], GetDate())) < '31' Then '' End)) AS 'Green'
,count((case when (DATEDIFF(day, [ArrivalDate], GetDate())) > '31' and (DATEDIFF(day, [ArrivalDate], GetDate())) < '91' then '' End)) AS 'Yellow'
,count((case when (DATEDIFF(day, [ArrivalDate], GetDate())) > '90' then '' End)) AS 'Red'
,DATEDIFF(day, [ApprovalCreationDate], GetDate()) AS 'DaysOldSinceApproval'
FROM dbo.WSUS_ComputerUpdateStatusWithApprovals
WHERE dbo.WSUS_ComputerUpdateStatusWithApprovals.IsApproved = 1
Group by dbo.WSUS_ComputerUpdateStatusWithApprovals.fulldomainname
,[ArrivalDate]
,dbo.WSUS_ComputerUpdateStatusWithApprovals.ApprovalCreationDate
,dbo.WSUS_ComputerUpdateStatusWithApprovals.title
ORDER BY dbo.WSUS_ComputerUpdateStatusWithApprovals.fulldomainname ASC
The result set comes out like this:
Where I would want it to say Computer Name and a Total count for each Green, Yellow, Red. So in this case, Computer Name, Green = 3, Yellow = 1 and Red = 1. I included the title and days old to show additional information.

If I followed you correctly, you just need to change the group by clause so it generates one row per fulldomainname:
select
fulldomainname AS [Computer Name],
sum(case when datediff(day, [ArrivalDate], GetDate()) between 1 and 30 then 1 else 0 end) AS Green,
sum(case when datediff(day, [ArrivalDate], GetDate()) between 30 and 90 then 1 else 0 end) AS Yellow,
sum(case when datediff(day, [ArrivalDate], GetDate()) > 90 then 1 else 0 end) AS Red
from dbo.WSUS_ComputerUpdateStatusWithApprovals
where IsApproved = 1
group by fulldomainname
order by fulldomainname ASC
Note that I changed the conditional aggregation logic to use sum() rather than count(), because it seems easier to follow.

Related

Return 1 row with various sums based on date?

Assume a table of purchase transactions with columns CustId, Amount, DatePosted where Amount is the value of the transaction, and DatePosted is a DATETIME value. Given a specific CustId, how would I write a select such that it returns a single row with the following columns: CustId, total value of transactions in the last 3 days, last 60 days, 1 year, 2 years (5 columns total).
Example table:
CustId
Amount
DatePosted
1234
698.02
2023-01-23Z12:34:56
1234
582.69
2022-12-15Z19:57:23
1234
7775.22
2022-12-02Z02:34:32
1234
18.72
2022-01-23Z12:34:56
1234
2.27
2021-01-23Z12:34:56
Expected output given the sample data above when searching using CustId=1234:
CustId
3-day Total
60-day Total
1 year Total
2 year Total
1234
698.02
9055.93
9074.65
9076.92
You could get all purchase data for the last 2 years, then using SUM with SQL CASE expression to calculate total value for each time-range.
SELECT
CustId,
SUM(CASE WHEN DatePosted >= Last3Day THEN Amount ELSE 0 END) AS [3-day Total],
SUM(CASE WHEN DatePosted >= Last60Day THEN Amount ELSE 0 END) AS [60-day Total],
SUM(CASE WHEN DatePosted >= Last1Year THEN Amount ELSE 0 END) AS [1 year Total],
SUM(CASE WHEN DatePosted >= Last2Year THEN Amount ELSE 0 END) AS [2 year Total]
FROM
<your data table>,
(SELECT
DATEADD(DAY, -3, GETDATE()) AS Last3Day,
DATEADD(DAY, -60, GETDATE()) AS Last60Day,
DATEADD(YEAR, -1, GETDATE()) AS Last1Year,
DATEADD(YEAR, -2, GETDATE()) AS Last2Year) timerange
WHERE DatePosted >= Last2Year
GROUP BY CustId;
Demo: http://sqlfiddle.com/#!18/9eecb/179880
This query assumes 2 year max. If you want to go further back then change the where clause as well. No need to use coalesce or a derived table. SQL server query planner may be smart enough to provide similar performance for all these solutions but this is easier to understand:
SELECT
CustId,
SUM(CASE WHEN DatePosted >= DATEADD(day, -3, GETDATE()) THEN Amount ELSE 0 END) AS [3-day Total],
SUM(CASE WHEN DatePosted >= DATEADD(day, -60, GETDATE()) THEN Amount ELSE 0 END) AS [60-day Total],
SUM(CASE WHEN DatePosted >= DATEADD(year, -1, GETDATE()) THEN Amount ELSE 0 END) AS [1 year Total],
SUM(Amount) AS [2 year Total]
FROM PurchaseTransactions
WHERE CustId = 1234 AND DatePosted >= DATEADD(year, -2, GETDATE())
GROUP BY CustId
This is set up so that you can set #CustID = null and the query will return results for all customers in the set.
EDIT: Updated my query below to give you more flexibility across your desired ranges should you wish to derive additional heuristics. (Counts, averages, etc.)
Also removed coalesce as it's simply not needed here.
DECLARE #CustID BIGINT;
SELECT table1.custID,
SUM([3Day].amount) AS [3DayTotal],
COUNT([3Day].amount) AS [3DayCount]
SUM([60Day].amount) AS [60DayTotal],
SUM([1Year].amount) AS [1YearTotal],
Sum([2Year].amount) AS [2YearTotal],
AVG([2Year].amount) AS [2YearAverage]
FROM table1 LEFT OUTER JOIN
(SELECT custID, Amount FROM table1 WHERE DatePosted > DATEADD(DAY, -3, GETDATE())) AS [3Day] ON table1.CustID = [3Day].CustID LEFT OUTER JOIN
(SELECT custID, Amount FROM table1 WHERE DatePosted > DATEADD(DAY, -60, GETDATE())) AS [60Day] ON table1.CustID = [60Day].CustID LEFT OUTER JOIN
(SELECT custID, Amount FROM table1 WHERE DatePosted > DATEADD(YEAR, -1, GETDATE())) AS [1Year] ON table1.CustID = [1Year].CustID LEFT OUTER JOIN
(SELECT custID, Amount FROM table1 WHERE DatePosted > DATEADD(YEAR, -2, GETDATE()) AS [2Year] ON table1.CustID = [2Year].CustID
WHERE table1.CustID = #CustID
OR #CustID IS NULL
GROUP BY table1.CustID

MS Sql Server statement inside COUNT

It does work in BigQuery but i do not get what am I doing incorrectly in MS SQL Server;
CASE
WHEN COUNT( RSM.RECEIVED_AT BETWEEN DATEADD(day, -84,UDATE.SQLDate) AND UDATE.SQLDate ) >= 1
AND COUNT(SAL.DATE BETWEEN DATEADD(day, -7, UDATE.SQLDate) AND UDATE.SQLDate) >=2
AND COUNT(SAL.DATE BETWEEN DATEADD(day, -14, UDATE.SQLDate) AND DATEADD(day, -7, UDATE.SQLDate)) >= 2
THEN 'True'
ELSE 'False' END AS AVAILABLE
What is the issue??
Incorrect syntax near ')'.
I suspect what you are after is:
CASE
WHEN COUNT(CASE WHEN RSM.RECEIVED_AT BETWEEN DATEADD(DAY, -84, UDATE.SQLDate) AND UDATE.SQLDate THEN 1 END) >= 1
AND COUNT(CASE WHEN SAL.[DATE] BETWEEN DATEADD(DAY, -7, UDATE.SQLDate) AND UDATE.SQLDate THEN 1 END) >= 2
AND COUNT(CASE WHEN SAL.[DATE] BETWEEN DATEADD(DAY, -14, UDATE.SQLDate) AND DATEADD(DAY, -7, UDATE.SQLDate) THEN 1 END) >= 2 THEN 'True'
ELSE 'False'
END AS AVAILABLE;
As I said in the comments: "You can't have a boolean expression within a COUNT (or any other aggregate function), it requires a scalar expression. "

SQL Server issue with select and group by with calculated columns

I have this SQL I am trying to build:
select
a.Name,
(SELECT COUNT(b.PlannedCollectionDate) WHERE b.PlannedCollectionDate < GETDATE()) AS Due,
(SELECT COUNT(b.PlannedCollectionDate) WHERE b.PlannedCollectionDate = GETDATE()) AS Today,
(SELECT COUNT(b.PlannedCollectionDate) WHERE b.PlannedCollectionDate = DATEADD(DAY, 1, GETDATE())) AS Expected,
(SELECT COUNT(b.PlannedCollectionDate) WHERE b.PlannedCollectionDate > DATEADD(DAY, 1, GETDATE())) AS Planned
from Centers AS a
INNER JOIN Collections AS b
ON a.Id = b.CenterId
GROUP BY a.Name
But I get an error:
Column 'Collections.PlannedCollectionDate' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
I know I could do something like this:
select
a.Name,
(SELECT COUNT(Id) FROM Collections WHERE CenterId = a.Id AND PlannedCollectionDate < GETDATE()) AS Due,
(SELECT COUNT(Id) FROM Collections WHERE CenterId = a.Id AND PlannedCollectionDate = GETDATE()) AS Today,
(SELECT COUNT(Id) FROM Collections WHERE CenterId = a.Id AND PlannedCollectionDate = DATEADD(DAY, 1, GETDATE())) AS Expected,
(SELECT COUNT(Id) FROM Collections WHERE CenterId = a.Id AND PlannedCollectionDate > DATEADD(DAY, 1, GETDATE())) AS Planned
from Centers AS a
But I assume that is slower because I have to do multiple selects from the same table (Collections).
So, my question is, what can I do to make my first query work? I don't think grouping by PlannedCollectionDate is right, because it will mess up my count
I think you want conditional aggregation:
select ce.Name,
SUM(CASE WHEN co.PlannedCollectionDate < GETDATE() THEN 1 ELSE 0 END) AS Due,
SUM(CASE WHEN co.PlannedCollectionDate = GETDATE() THEN 1 ELSE 0 END) AS Today,
SUM(CASE WHEN co.PlannedCollectionDate = DATEADD(DAY, 1, GETDATE()) THEN 1 ELSE 0 END) AS Expected,
SUM(CASE WHEN co.PlannedCollectionDate > DATEADD(DAY, 1, GETDATE()) THEN 1 ELSE 0 END) AS Planned
from Centers ce join
Collections co
on ce.Id = co.CenterId
group by ce.Name;
This implements what you have written. Do note the use of meaningful table aliases.
However, it will not do what you want, because GETDATE() has a time component. To fix that, convert it to a date:
select ce.Name,
SUM(CASE WHEN co.PlannedCollectionDate < CONVERT(DATE, GETDATE()) THEN 1 ELSE 0 END) AS Due,
SUM(CASE WHEN co.PlannedCollectionDate = CONVERT(DATE, GETDATE()) THEN 1 ELSE 0 END) AS Today,
SUM(CASE WHEN co.PlannedCollectionDate = DATEADD(DAY, 1, CONVERT(DATE, GETDATE())) THEN 1 ELSE 0 END) AS Expected,
SUM(CASE WHEN co.PlannedCollectionDate > DATEADD(DAY, 1, CONVERT(DATE, GETDATE())) THEN 1 ELSE 0 END) AS Planned
from Centers ce join
Collections co
on ce.Id = co.CenterId
group by ce.Name;
Note that this assumes that PlannedCollectionDate does not have a time component.

Use a date based on field value

I need to find a way to relate two sets of orders. Essentially, and inbound versus outbound analysis for a report. A load balance to see when I need to organize more pickups or deliveries.
I need to pull the delivery_date when billing_group = 3
I need to pull the origin_date when billing_group = 4
Hope this made some sense. Thank you all!
select convert(varchar,Delivery_Date,101) as 'Delivery_Date',
convert(varchar,Origin_Date,101) as 'Origin_Date',
sum(case when billing_group = '3' then 1 else 0 end) as 'OR to WA',
sum(case when billing_group = '4' then 1 else 0 end) as 'WA to OR',
count(*) as Total_Orders
from orders
where Date >= dateadd(day, datediff(day, 0, GetDate()) - 30, 0) and
billing_group in ('3','4')
group by Date
order by date desc
Would it be possible to combine these two separate queries into one to provide a the following columns? date, OR to WA, and WA to OR (like in the example above) where the date (regardless of origin or delivery date is used
select convert(varchar,Delivery_Date,101) as 'Date',
sum(case when billing_group = '3' and delivery_date >= dateadd(day,datediff(day, 0, GetDate()) - 30, 0) then 1 else 0 end) as 'OR to WA'
from orders
where delivery_Date >= dateadd(day, datediff(day, 0, GetDate()) - 20, 0) and billing_group in ('3')
group by delivery_date
order by date desc
select convert(varchar,Origin_Date,101) as 'Date',
sum(case when billing_group = '4' and origin_date >= dateadd(day, datediff(day, 0, GetDate()) - 30, 0)then 1 else 0 end) as 'WA to OR'
from orders
where origin_Date >= dateadd(day, datediff(day, 0, GetDate()) - 20, 0) and billing_group in ('4')
group by origin_Date
order by date desc
I think you need to group by convert(varchar,Delivery_Date,101) instead of date.
When you use aggregate function you need to add non-aggregate colunms in group by.
select convert(varchar,Delivery_Date,101) as 'Delivery_Date',
sum(case when billing_group = '3' then 1 else 0 end) as 'OR to WA',
sum(case when billing_group = '4' then 1 else 0 end) as 'WA to OR',
count(*) as Total_Orders
from orders
where Date >= dateadd(day, datediff(day, 0, GetDate()) - 30, 0) and
billing_group in ('3','4')
group by convert(varchar,Delivery_Date,101)
order by date desc
EDIT
I saw you edit your question, you can try to use UNION ALL
SELECT * FROM (
select convert(varchar,Delivery_Date,101) as 'Date',
sum(case when billing_group = '3' and delivery_date >= dateadd(day,datediff(day, 0, GetDate()) - 30, 0) then 1 else 0 end) as 'OR to WA'
from orders
where delivery_Date >= dateadd(day, datediff(day, 0, GetDate()) - 20, 0) and billing_group in ('3')
group by delivery_date
UNION ALL
select convert(varchar,Origin_Date,101) as 'Date',
sum(case when billing_group = '4' and origin_date >= dateadd(day, datediff(day, 0, GetDate()) - 30, 0)then 1 else 0 end) as 'WA to OR'
from orders
where origin_Date >= dateadd(day, datediff(day, 0, GetDate()) - 20, 0) and billing_group in ('4')
group by origin_Date
) t1
ORDER BY Date desc

Combine queries to show one set of results

I have two separate queries where I need to match up and combine together based on a date value.
select convert(varchar,Delivery_Date,101) as 'Date',
sum(case when billing_group = '3' and delivery_date >= dateadd(day,datediff(day, 0, GetDate()) - 30, 0) then 1 else 0 end) as 'OR to WA'
from orders
where delivery_Date >= dateadd(day, datediff(day, 0, GetDate()) - 30, 0) and billing_group in ('3')
group by delivery_date
order by date desc
select convert(varchar,Origin_Date,101) as 'Date',
sum(case when billing_group = '4' and origin_date >= dateadd(day, datediff(day, 0, GetDate()) - 30, 0)then 1 else 0 end) as 'WA to OR'
from orders
where origin_Date >= dateadd(day, datediff(day, 0, GetDate()) - 30, 0) and billing_group in ('4')
group by origin_Date
order by date desc
Please note that I am using a different date value (delivery_date) in the first query and a different for the second (origin_date)
Thanks for the help!!
I think you just want conditional aggregation, but your query offers other room for improvement:
select convert(varchar(255), v.thedate, 101) as [Date],
sum(case when o.billing_group = 3 then 1 else 0 end) as [OR to WA],
sum(case when o.billing_group = 4 then 1 else 0 end) as [WA to OR]
from orders o cross apply
(values (case when o.billing_group = 3 then delivery_date else o.origin_date end)
) v(the_date)
where v.thedate >= dateadd(day, -30, cast(getdate() as date)) and
o.billing_group in (3, 4)
group by convert(varchar(255), v.thedate, 101)
order by v.thedate desc
Notes:
Never use varchar() without a length parameter. The length varies by context and it might not do what you expect.
I am guessing that billing_group is a number. If so, don't use single quotes. Do use single quotes if I'm wrong.
I cannot tell what the data type of delivery_date is. It is safer to aggregate by the full expression, rather than just the column.
Do not use single quotes for column aliases. Only use single quotes for string and date constants.
There is no reason to use the "trick" of adding days to 0 to remove a time component. Instead, just convert to date.
The condition on delivery_date does not have to appear in both the case expression and the where clause.
Try using UNION and adding the missing columns
then do group by and ordering
select * from (
convert(varchar,Delivery_Date,101) as 'Date',
sum(case when billing_group = '3' and delivery_date >= dateadd(day,datediff(day, 0, GetDate()) - 30, 0) then 1 else 0 end) as 'OR to WA',
0 as 'WA to OR'
from orders
where delivery_Date >= dateadd(day, datediff(day, 0, GetDate()) - 30, 0) and billing_group in ('3')
-- group by delivery_date
--- order by date desc
UNION
select convert(varchar,Origin_Date,101) as 'Date',
0 as 'OR to WA'
sum(case when billing_group = '4' and origin_date >= dateadd(day, datediff(day, 0, GetDate()) - 30, 0)then 1 else 0 end) as 'WA to OR'
from orders
where origin_Date >= dateadd(day, datediff(day, 0, GetDate()) - 30, 0) and billing_group in ('4')
--group by origin_Date
--order by date desc
) group by [Date]
order by [Date] desc