CASE in WHERE Clause - sql

Below is my current SQL Server 2012 query. Basically I want the information from the last business day, but on Monday, I want it to pull Friday's info instead of Sunday. This is what I have so far in my query but it won't accept it.
USE [LetterGeneration]
SELECT
g.LetterGenerationPrintJobId,
CONVERT(CHAR(12), r.CreatedDate, 101) AS CreatedDate,
YEAR(r.CreatedDate) AS Year,
MONTH(r.CreatedDate) AS Month,
DAY(r.CreatedDate) AS Day,
CASE
WHEN DATEPART(dw, r.CreatedDate) = 1
THEN 1
WHEN DATEPART(dw, r.CreatedDate) = 7
THEN 1
ElSE 0
END AS Weekend,
s.LetterGenerationStatusId AS Status,
COUNT(g.LetterGenerationId) AS LetterCount,
SUM(g.LetterPageCount) AS PageCount,
t.IsLitigationCoverLetterAllowed,
CASE
WHEN g.CarrierTrackingNumber LIKE '%1ZE%'
THEN 1
WHEN g.CarrierTrackingNumber LIKE '921489%'
THEN 2
WHEN g.CarrierTrackingNumber LIKE '917190%'
THEN 2
ELSE 3
END AS CarrierType
FROM
[LetterGenerationTemplateRequest] AS R
INNER JOIN
[LetterGenerationTemplate] AS T ON t.[LetterGenerationTemplateId] = r.LetterGenerationTemplateId
INNER JOIN
LetterGeneration G ON g.LetterGenerationTemplateRequestId = r.LetterGenerationTemplateRequestId
INNER JOIN
LetterGenerationStatus S ON g.LetterGenerationStatusId = s.LetterGenerationStatusId
WHERE
(CASE
WHEN (DATENAME(dw,GETDATE()) = 'Monday')
THEN (DATEDIFF(d, r.CreatedDate, GETDATE()) = 3)
ELSE (DATEDIFF(d, r.CreatedDate, GETDATE()) = 1)
END)
AND t.[TemplateKey] NOT LIKE '%PLTV1%'
AND s.LetterGenerationStatusId = 19
ORDER BY
r.CreatedDate DESC, g.LetterGenerationPrintJobId DESC
What am I missing or misunderstanding about my WHERE clause in order to make it work in the way I'm thinking?
Thanks

Maybe convert to a regular AND/OR?
WHERE (
((DATENAME(dw,GETDATE()) = 'Monday') AND (DATEDIFF(d, r.CreatedDate, GETDATE()) = 3))
OR
(DATEDIFF(d, r.CreatedDate, GETDATE()) = 1)
)
....

What am I missing or misunderstanding about my WHERE clause in order to make it work in the way I'm thinking?
Though you haven't given the error message you're getting, I'm sure it's syntax related because you're putting the test INSIDE the result of the case, not outside it
You're writing:
WHERE CASE WHEN it_is_monday THEN data_date = friday ELSE data_date = yesterday END
You should be writing:
WHERE data_date = CASE WHEN it_is_monday THEN friday ELSE yesterday END
Essentially: you're not supposed to use case/when in a where clause to do your "column = something" comparison and return you true or false, you're supposed to use it to just return the "something" you compare against "column" else in order to get your true or false
The other answers focus on "giving you a working solution"; this answer focuses on telling you what was going wrong with your thought processes re your original query
Here's a simpler example:
--wrong syntax to search a table full of cats (4 legs) and people (2 legs)
WHERE CASE WHEN animal_type = 'cat' THEN legs = 4 ELSE legs = 2 END
--right syntax
WHERE limbs = CASE WHEN animal_type = 'cat' THEN 4 ELSE 2 END

Ignoring holidays for a second, and assuming you have at least one record for every date, something like this should work.
where cast(createdDate as date) =
(select max(createdDate )
from table
where createdDate < cast(getDate() as date
and dateName(dw, createdDate in ('Monday' etc)
)

In order to maintain SARGability(able to do a seek against an index) you want to make sure the table columns in the predicate aren't included in any functions.
The following should work and maintain SARGability...
WHERE
r.CreatedDate = CASE
WHEN DATEPART(dw, getdate) = 2
THEN DATEADD(dd, -3, CAST(GETDATE() AS DATE))
ELSE CAST(GETDATE() AS DATE)
END
HTH,
Jason

Related

Aging - Calendar and Business Days Different Records of Data

I am currently using SQL Server 2012 and I have an aging question. I tried to include some sample data below with 4 records.
FileNumber FileType CompletedDate
90440 Internal 8/11/2017
90440 Strategy NULL
90441 Internal 8/10/2017
90441 Strategy NULL
A Strategies' aging is calculated from the Internal FileType's Completed Date all the way up to the Strategy FileType's Completed Date. Every FileNumber can have multiple FileTypes associated with it. If the Strategy's Completed Date is NULL, then it would be from Internal FileType's Completed Date all the way up to today's date or GETDATE().
So I'm trying to show Count of Pending Strategies and their current aging in Business and Calendar Days...so essentially I would want the data to return like this.
File Number FileType AgeBusiness AgeCalendar
90440 Strategy 2 4
90441 Strategy 3 5
Any clue on how I would go about this? Any help is appreciated.
Something like this may work but I'm not 100% sure since it depends on what sort of Filetypes are in the table, whether you are accounting for holidays, what happens when Strategy (or whatever is the latest file type) has a value for completed date.
;WITH Internal AS
(
select FileNumber, FileType,
DATEDIFF(day,CompletedDate,getdate())- (datediff(wk, CompletedDate, getdate()) * 2) -
case when datepart(dw, CompletedDate) = 1 then 1 else 0 end +
case when datepart(dw, getdate()) = 1 then 1 else 0 end as AgeBusiness,
DATEDIFF(day,CompletedDate,getdate()) as AgeCalendar
from #test
where FileType = 'Internal'
)
select t.FileNumber, t.FileType, i.AgeBusiness, i.AgeCalendar
from #test t
inner join Internal i on
(t.FileNumber = i.FileNumber)
where CompletedDate is null
the problem with the above is it wouldn't show a row if Strategy (or whatever is the latest file type) HAS a completed date. Therefore you may want something like
;WITH Internal AS
(
select FileNumber, FileType,
DATEDIFF(day,CompletedDate,getdate())- (datediff(wk, CompletedDate, getdate()) * 2) -
case when datepart(dw, CompletedDate) = 1 then 1 else 0 end +
case when datepart(dw, getdate()) = 1 then 1 else 0 end as AgeBusiness,
DATEDIFF(day,CompletedDate,getdate()) as AgeCalendar
from #test
where FileType = 'Internal'
), Latest AS
(
select FileNumber, FileType, RANK() OVER
(PARTITION BY FileNumber ORDER BY COALESCE(CompletedDate,GETDATE()) DESC) AS rnk
from #test
)
select l.FileNumber, l.FileType, i.AgeBusiness, i.AgeCalendar
from Latest l
inner join Internal i on
(l.FileNumber = i.FileNumber)
where rnk = 1
where it would show the row regardless of if it's null or not, but at the expense of adding a RANK() which is more intensive.
You don't need a CTE but I included it to make it more readable.
WITH DateRanges AS (
SELECT yt.FileNumber
,yt.FileType
,SELECT TOP 1 CompletedDate FROM YourTable WHERE yt.FileNumber = FileNumber AND FileType = 'Internal' AS InternalCompletedDate
,CASE WHEN yt.CompletedDate IS NULL THEN CAST(GETDATE() AS DATE) ELSE yt.CompletedDate END AS StrategyCompletedDate -- I forgot the NULL part too...
FROM YourTable yt
WHERE yt.FileType = 'Strategy'
)
SELECT FileNumber
,FileType
,(DATEDIFF(dd,InternalCompletedDate, StrategyCompletedDate) + 1)
-(DATEDIFF(wk, InternalCompletedDate, StrategyCompletedDate) * 2)
-(CASE WHEN DATENAME(dw, InternalCompletedDate) = 'Sunday' THEN 1 ELSE 0 END)
-(CASE WHEN DATENAME(dw, StrategyCompletedDate) = 'Saturday' THEN 1 ELSE 0 END) AS AgeBusiness
,DATEDIFF(dd, InternalCompletedDate, StrategyCompletedDate) AS AgeCalendar
FROM DateRanges
Edit: Should be good now. I rushed it a bit.

How to return count in 2 columns?

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.

ms sql 2012 case syntax

I have table survey_status_history which is having columns caseId, strsurveystatus, dtcreated.
I want to fetch record from table for all status but when status = 'pending', for this case query should return record for last five days only.
Below is my query.
select *
from survey_status_history ssh
where nisactive = 1
and case when ssh.strsurveystatus = 'pending'
then ssh.dtcreated > DATEADD(DAY, 5 , GETDATE())
end
But I am getting error near >
Kindly suggest the changes in query.
Thanks in advance.
SQL Server doesn't understand a boolean type. I would recommend writing this without the case:
select *
from survey_status_history ssh
where nisactive = 1 and
(ssh.strsurveystatus <> 'pending' or
ssh.dtcreated > DATEADD(DAY, 5 , GETDATE())
);
This assumes that strsurveystatus is never NULL. If it is NULL the logic is slightly more complicated.
Hope, this suits your requirement.
SELECT *
FROM SURVEY_STATUS_HISTORY SSH
WHERE NISACTIVE = 1 AND
(SSH.STRSURVEYSTATUS <> 'PENDING' -- if not pending
OR(SSH.STRSURVEYSTATUS = 'PENDING' AND SSH.DTCREATED >= DATEADD(DAY, -5 , GETDATE()))) -- if pending and last 5 days

Incremental Load Issue

I have a query like so:
SELECT Col1, Col2, Col3, ColDate
FROM TableA
WHERE DAY(ColDate) = 1
AND MONTH(ColDate) = MONTH(CASE WHEN DATEPART(HOUR, GETDATE()) < 16 THEN GETDATE() - 1 ELSE GETDATE()END)
AND YEAR(ColDate) = YEAR(CASE WHEN DATEPART(HOUR, GETDATE()) < 16 THEN GETDATE() - 1 ELSE GETDATE()END)
The query above pulls data daily into another table. Now, the issue with this query is that we have to manually change the DAY(ColDate) =? in the WHERE Clause everyday to pull the right data. How do I set my WHERE Clause to check the day in that month and pull data accordingly?
Is this what you are looking for?
WHERE DAY(colDate) = DAY(GETDATE()) AND
. . .
Or, perhaps:
WHERE DAY(colDate) = DAY(CASE WHEN DATEPART(HOUR, GETDATE()) < 16 THEN GETDATE() - 1 ELSE GETDATE() END) AND

Returning multiple queries as a single result set against the same database columns

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.