SQL conditional WHERE simplification - sql

I currently have a statement in my WHERE clause like this:
AND ((#includeExpired = 0 AND lic.[DateExpiresUtc] > GETDATE())
OR
(#includeExpired = 1 AND lic.[DateExpiresUtc] <> GETDATE())
)
Which just looks ugly, I've tried just including a simpler version of the statement like:
(#includeExpired = 0 AND lic.[DateExpiresUtc] > GETDATE())
But when #includeExpired is 1 it all fails to select anything.
Is there a better way of doing this?

According to your variabel name #includeExpired, which implies that this variable decides about additionally including expired records in the result set, the check in the second part, lic.[DateExpiresUtc] <> GETDATE(), is not necessary and the other check in the first part is not necessary.
Try this:
AND (lic.[DateExpiresUtc] > GETDATE()) OR (#includeExpired = 1)

Just one idea.
(CASE
WHEN #includeExpired = 1 THEN 1
WHEN #includeExpired = 0 AND lic.[DateExpiresUtc] > GETUTCDATE() THEN 1
ELSE 0 END) = 1

Related

How to use case to construct a conditional update

I have to write an update query. If the special_member account is not cancelled then in the where clause I have to use this condition by adding a grace period of 15 days to the expiry date and compare it today's date:
Convert(date,MEMBER_EXPIRY_DATE + 15) >= Convert(date,GETDATE())
If the membership is cancelled then I have to compare the actual expiry date with today's date. This is my full query:
UPDATE SPECIAL_MEMBER SET SAVINGS_PERCENT = 10, ORDER_COUNT = 1
WHERE SPECIAL_MEMBER = '4382' AND CASE WHEN (CANCELLED = 0) THEN
Convert(date,MEMBER_EXPIRY_DATE + 15) >= Convert(date,GETDATE())
ELSE (Convert(date,MEMBER_EXPIRY_DATE) >= Convert(date,GETDATE())) END
When I execute it I am getting:
Incorrect syntax near '>'.
Its a case expression, not a statement, as as such it can only return a value i.e. cannot contain any conditions. Just move the value you are comparing to outside the case e.g.
UPDATE SPECIAL_MEMBER SET
SAVINGS_PERCENT = 10
, ORDER_COUNT = 1
WHERE SPECIAL_MEMBER = '4382'
AND CASE WHEN CANCELLED = 0
THEN CONVERT(DATE,DATEADD(DAY,15,MEMBER_EXPIRY_DATE))
ELSE CONVERT(DATE,MEMBER_EXPIRY_DATE) END >= CONVERT(DATE,GETDATE());
That however is not sargable i.e. cannot make use of any indexes on MEMBER_EXPIRY_DATE so I would recommend switching the logic around to
UPDATE SPECIAL_MEMBER SET
SAVINGS_PERCENT = 10
, ORDER_COUNT = 1
WHERE SPECIAL_MEMBER = '4382'
AND MEMBER_EXPIRY_DATE >= CONVERT(DATE,DATEADD(DAY,CASE WHEN CANCELLED = 0 THEN -15 ELSE 0 END,GETDATE()))
Notes:
Dates don't naturally add, use the dateadd function.
There is nothing dynamic about this - dynamic SQL is something quite different
Using a good layout and consistent casing makes a big difference in being able to read your SQL
You can ignore the CASE and use like below:
UPDATE SPECIAL_MEMBER
SET SAVINGS_PERCENT = 10
, ORDER_COUNT = 1
WHERE SPECIAL_MEMBER = '4382'
AND ( CANCELLED = 0 AND DATEADD(dd,15,CONVERT(DATE, MEMBER_EXPIRY_DATE)) >= CONVERT(DATE,GETDATE())
OR CANCELLED != 0 AND CONVERT(DATE,MEMBER_EXPIRY_DATE) >= CONVERT(DATE,GETDATE()))

How to write a T-SQL query which can return 0 rows as part of a case statement

I am trying to write a query which checks how many records arrived in the last x days so that I can send out an alert if no new records have arrived in that window.
To that end I want a query which will check the table and return either 1 row stating no files were detected if there is a problem or no rows if everything is ok. The reason I want there to be no rows is because the downstream program will treat any returned rows as an error having been detected and alert accordingly.
select 'Null check' as id,
case when
count(*) > 0 then NULL
else 'No files detected' end as Message
from TABLE where LASTUPDATEDATE > dateadd(d, -1, getdate())
This query works if an error is detected but not in the correct case as it still returns a row. How can I rewrite it so that it doesn't return anything? Thanks!
For performance reasons, I would recommend writing this as:
select 'Null check' as id, 'No files detected' as Message
from (select top (1) t.*
from table t
where lastupdatedate > dateadd(day, -1, getdate())
) t
having count(*) = 0;
This saves SQL Server from actually having to count a bunch of rows if when things are fine.
Seems to be a task for NOT EXISTS:
select 'Null check' as id, 'No files detected' as Message
where not exists
(
select *
from tab
where lastupdatedate > dateadd(day, -1, getdate())
)
The trick is to use having count(*) = 0
select 'Null check' as id, 'No files detected' as Message
from TABLE
where LASTUPDATEDATE > dateadd(d, -1, getdate())
having count(*) = 0
Demo
SQL Fiddle - count() = 0
SQL Fiddle - count() > 0
You can accomplish this with a CASE and subquery:
SELECT [Message] =
CASE
WHEN COALESCE(SELECT COUNT(*) FROM [TABLE] WHERE [LASTUPDATEDATE] > DATEADD(d, -1, GETDATE())), 0) > 0
THEN 'No files detected'
ELSE NULL
END ;

CASE in WHERE Clause

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

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

Display only needed results in SQL

I need a little assistance in finishing this query. Here is what I have so far:
select
(select count(fileName)
from PDFFile
where dateTime > cast(getdate() as date)
and stateId = 17) AS "Files on SFTP"
,
(select count(fileName)
from PDFFile
where dateTime > cast(getdate() as date)
and stateId = 12) AS "Files Pass"
,
((select count(fileName)
from PDFFile
where dateTime > cast(getdate() as date)
and stateId = 17)
-
(select count(fileName)
from PDFFile
where dateTime > cast(getdate() as date)
and stateId = 12)) AS "Diff"
This is going to give me 3 columns of results. First result will be a number, second will be a number and the third will be the diff. There may even be a better way to write this but I'm still a novice. Hint: There is an entry in the DB for each state:
fileName |dateTime | stateID
--------+---------+-----------------+---------
abc.pdf | 2013-12-17 12:03:14.597 | 17
abc.pdf | 2013-12-17 12:06:23.096 | 12
xyz.pdf | 2013-12-17 12:09:16.583 | 17
xyz.pdf | 2013-12-17 12:10:19.823 | 12
Anyways for the finale...
I need to have a 4th column or a separate query (possible to UNION?) that pulls the fileNames based off the results in the diff.
Hypothetically if the diff is 40, the 4th column or separate query should list the 40 names. At times the diff may be negative so again hypothetically speaking if its -40 it should list the 40 names.
Assistance is greatly appreciated. Thank you!
You can greatly simplify your query using conditional aggregation:
select sum(case when dateTime > cast(getdate() as date) and stateId = 17 then 1 else 0
end) as "Files on SFTP",
sum(case when dateTime > cast(getdate() as date) and stateId = 12 then 1 else 0
end) AS "Files Pass",
(sum(case when dateTime > cast(getdate() as date) and stateId = 17 then 1 else 0
end) -
sum(case when dateTime > cast(getdate() as date) and stateId = 12 then 1 else 0
end)
) as diff
from PDFFile;
To get the list of files that are in the first group but not the second requires a bit more logic. The problem is that the unit of aggregation is at the file level.
select PDFFile
from PDFFile
group by PDFFile
having sum(case when dateTime > cast(getdate() as date) and stateId = 17 then 1 else 0
end) > 0 and
sum(case when dateTime > cast(getdate() as date) and stateId = 12 then 1 else 0
end) = 0;
Each part of the having clause counts the number of rows -- for each file -- that match the two conditions. You want at least one row that matches the first condition (hence > 0) and no rows that match the second (= 0).
This type of "combine row data into one column" question comes up quite a lot on Stack Overflow and although it has its place it's often easier and more efficient to solve the problem in another way.
For example, it's a lot easier to ask SQL to "give me all the filenames where stateid = 17", return them to your app and then get the app to display them. It may also be that your user doesn't want to see them until there is a particular summary line that is of interest to them that they need to drill down into further. Think of email as an example - you may only need to look at the 30 character subject line and know you don't need to download the 1Mb email body.
For your first question though there is a lot easier (and more efficient) way to write your query. Note that this example is untested
select
sum(case when stateId = 17 then 1 else 0 end) as "Files on SFTP",
sum(case when stateId = 12 then 1 else 0 end) as "Files Pass",
sum(case when stateId = 17 then 1 else 0 end) -
sum(case when stateId = 12 then 1 else 0 end) as "Diff",
from
PdfFile
where
datetime > getdate()
I'm using CASE here to prevent having to do three separate sub-queries. Sub-queries are inefficient. CASE isn't great but it's faster than sub-queries. I've also placed your datetime check at the bottom of the query as a WHERE as it was common to each of your checks.