CASE / IF Statement to declare variable based on parameter value - sql

Been researching 'CASE' syntax and I think I'm close to getting what I need here but something is off. SSMS didn't like the way I wrote my query with CASE and I ended up confusing myself so I've written the sample below as if it were an 'IF' statement just so I can express what I'm trying to achieve. Any hints or tips would certainly be appreciated.
The goal is to have a parameter list of date ranges ("Last Week", "Month to Date", etc) that when selected will pass two values (upper and lower date limits) to the WHERE statement.
DECLARE #Param1 VARCHAR(20)
-- Param1 will be set when the user selects a string value, e.g. "Last Month"
DECLARE #Date1 DATE, #Date2 DATE
-- Date1 & Date2 will be the respective lower and upper date boundaries used in the WHERE statement.
IF #Param1 = 'Last Month'
THEN
SET #Date1 = CONVERT(DATE,DATEADD(dd,-DAY(GETDATE())+1,DATEADD(mm,-1,GETDATE())))
SET #Date2 = CONVERT(DATE,DATEADD(dd,-DAY(GETDATE())+1,GETDATE()))
ELSE IF #Param1 = 'Current Month'
THEN
SET #Date1 = CONVERT(DATE,DATEADD(dd,-DAY(GETDATE())+1,GETDATE()))
SET #Date2 = CONVERT(DATE,GETDATE())
ELSE
-- Default to yesterday if nothing selected by user
SET #Date1 = CONVERT(DATE,GETDATE()-1)
SET #Date2 = CONVERT(DATE,GETDATE())
END IF

You would do this with two case statements:
select #Date1 = (case when #Param1 = 'Last Month'
then CONVERT(DATE,DATEADD(dd,-DAY(GETDATE())+1,DATEADD(mm,-1,GETDATE())))
when #Param1 = 'Current Month'
then CONVERT(DATE,DATEADD(dd,-DAY(GETDATE())+1,GETDATE()))
else CONVERT(DATE,GETDATE()-1)
end);
select #Date2 = (case when #Param1 = 'Last Month'
then CONVERT(DATE,DATEADD(dd,-DAY(GETDATE())+1,GETDATE()))
when #Param1 = 'Current Month'
then CONVERT(DATE,GETDATE())
else CONVERT(DATE,GETDATE())
end);

Related

Subselect inside Case in sql?

I have this pretty code of mine, written by using "if". I want to know if its possible to do just the same thing, using "case". Inside "WeekNumber" are just numbers from 1 to 53.
I want to subselect inside case function
declare #currYear int = datepart(year,getdate());
declare #date date = datefromparts(#currYear,1,1);
--declare #date date = '2022-01-01'
declare #firstDayOfWeekToLast date = DATEADD(day,-datepart(weekday,#date) + 2,#date)
declare #lastDayOfWeekToLast date = DATEADD(day,-datepart(weekday,#date) + 6,#date)
declare #firstDayOfWeekToNew date = DATEADD(day,-datepart(weekday,#date) - 5,#date)
declare #lastDayOfWeekToNew date = DATEADD(day,-datepart(weekday,#date) - 1,#date)
select DATEPART(WEEKDAY,#date)
if(DATEPART(weekday,#date) > 5)
begin
select format(DATEADD(week,WeekNumber,#firstDayOfWeekToLast),'dd.MM.yyyy') as 'First day of Week',
format(DATEADD(week,WeekNumber,#lastDayOfWeekToLast),'dd.MM.yyyy') as 'Last day of Week'
from WEEKNUMBER
end
else if(DATEPART(weekday,#date) = 1)
begin
select format(DATEADD(week,WeekNumber-1,#firstDayOfWeekToLast),'dd.MM.yyyy') as 'First day of Week',
format(DATEADD(week,WeekNumber-1,#lastDayOfWeekToLast),'dd.MM.yyyy') as 'Last day of Week'
from WEEKNUMBER
end
else
begin
select format(DATEADD(week,WeekNumber,#firstDayOfWeekToNew),'dd.MM.yyyy') as 'First day of Week',
format(DATEADD(week,WeekNumber,#lastDayOfWeekToNew),'dd.MM.yyyy') as 'Last day of Week'
from WEEKNUMBER
end
I am not sure if I completely understand your question but you could try the query below.
declare #currYear int = datepart(year,getdate());
declare #date date = datefromparts(#currYear,1,1);
--declare #date date = '2022-01-01'
declare #firstDayOfWeekToLast date = DATEADD(day,-datepart(weekday,#date) + 2,#date)
declare #lastDayOfWeekToLast date = DATEADD(day,-datepart(weekday,#date) + 6,#date)
declare #firstDayOfWeekToNew date = DATEADD(day,-datepart(weekday,#date) - 5,#date)
declare #lastDayOfWeekToNew date = DATEADD(day,-datepart(weekday,#date) - 1,#date)
--The changes I made start below.
SELECT
WEEKNUMBER,
CASE
WHEN (DATEPART(weekday,#date) > 5) THEN format(DATEADD(week,WeekNumber,#firstDayOfWeekToLast),'dd.MM.yyyy')
WHEN (DATEPART(weekday,#date) = 1) THEN format(DATEADD(week,WeekNumber-1,#firstDayOfWeekToLast),'dd.MM.yyyy')
ELSE format(DATEADD(week,WeekNumber,#firstDayOfWeekToNew),'dd.MM.yyyy')
END 'First day of Week',
CASE
WHEN (DATEPART(weekday,#date) > 5) THEN format(DATEADD(week,WeekNumber,#lastDayOfWeekToLast),'dd.MM.yyyy')
WHEN (DATEPART(weekday,#date) = 1) THEN format(DATEADD(week,WeekNumber-1,#lastDayOfWeekToLast),'dd.MM.yyyy')
ELSE format(DATEADD(week,WeekNumber,#lastDayOfWeekToNew),'dd.MM.yyyy')
END 'Last day of Week'
FROM WEEKNUMBER

Select between two dynamic (variable) dates SQL

I have a query that runs once a day that I would like to share however I need to remove the part where the other users in my team will have to edit it. Essentially, it's run Monday thru Friday. I want, if today is Monday, give me the last 3 days worth of data. Any other day, just give me yesterday's data.
So far this is what I have:
Update: They are all strings, so now I get the following error.
"Incorrect syntax near the keyword 'BETWEEN'."
DECLARE #daychecker varchar(max) = FORMAT(GETDATE(), 'dddd')
DECLARE #daterange0 varchar(max)
DECLARE #daterange1 varchar(max) = FORMAT(GETDATE()-3, 'yyyy-MM-dd')
DECLARE #daterange2 varchar(max) = FORMAT(GETDATE()-1, 'yyyy-MM-dd')
IF #daychecker = 'Wednesday'
BEGIN
SET #daterange0 = BETWEEN #daterange1 AND #daterange2
END
ELSE
BEGIN
SET #daterange0 = FORMAT(GETDATE()-1, 'yyyy-MM-dd')
END
SELECT #daterange0;
The result for today as an example should return yesterday's date. But that doesn't work. I will consider all options including hardcoding some sort of master start date that we can count from like maybe the start of the year or something.
You're much better off defining 2 dates, a start date and end date, and filtering your query based on them. EDIT: I'm now unsure if you want actual dates for filtering data, or a label for a report. I modified my answer to include the latter, use whichever you want and ignore the other ...
DECLARE #DateStart DATE
DECLARE #DateEnd DATE
DECLARE #LableRange varchar(max)
SELECT DATEPART(WEEKDAY, GETDATE()) --Sun=1, Mon=2, ...
IF DATEPART(WEEKDAY, GETDATE()) = 2 BEGIN
SET #DateStart = DATEADD (DAY, -5,GETDATE())
SET #DateEnd = DATEADD (DAY, -2,GETDATE())
SET #LableRange = CONCAT(FORMAT(#DateStart, 'yyyy-MM-dd'), ', '
, FORMAT(DATEADD(day,1,#DateStart), 'yyyy-MM-dd'), ', '
, FORMAT(DATEADD(day,2,#DateStart), 'yyyy-MM-dd'))
-- or maybe this format is better
--SET #LableRange = CONCAT('BETWEEN '
--, FORMAT(#DateStart, 'yyyy-MM-dd'), ' AND '
--, FORMAT(DATEADD(day,2,#DateStart), 'yyyy-MM-dd'))
END ELSE BEGIN
SET #DateStart = DATEADD (DAY, -1,GETDATE())
SET #DateEnd = GETDATE()
SET #LableRange = FORMAT(#DateStart, 'yyyy-MM-dd')
END
SELECT #LableRange
SELECT * FROM SomeTable as T
WHERE T.TestDate < #DateEnd AND T.TestDate >= #DateStart
Note that this works even if the date you are filtering on is a datetime instead of pure date.

Is there a way to shorten this script?

Can anyone please help me figure out a more convenient and efficient (shortest) way of writing the below script? The reason I'm doing this is because I will end up using the script on SSRS.
On SSRS, there will be two parameters and both are set to take blank values.
If the user running SSRS indicated only the Starting date parameter, then my report should give any date >= the starting date.
If the user put dates on both #startingdate and #endingdate, my script will search only those that are between #startingdate and #endingdate.
Do you guys kinda get what I'm trying to accomplish? I have an SSRS report similar to this scenario but it's very cumbersome to update because if I update one of the statement, I would have to do it on the other ones. Also the script is big and repetitive because of this.
If you could help me figure out a better way to do shorten or make it less cumbersome to code, please let me know, thanks!
Below is a sample script that I use:
DECLARE #STARTINGDATE
DECLARE #ENDINGDATE
SET #STARTINGDATE = '10/01/2013'
SET #ENDINGDATE = '10/05/2013'
CASE
WHEN #STARTINGDATE <> '' AND #ENDINGDATE <> ''
SELECT * FROM SALESTABLE
WHERE SALESDATE BETWEEN #STARTINGDATE AND #ENDINGDATE
END
WHEN #STARTINGDATE = '' AND #ENDINGDATE = ''
SELECT * FROM SALESTABLE
END
WHEN #STARTING <> '' AND #ENDINGDATE = ''
SELECT * FROM SALESABLE
WHERE SALESDATE >= #STARTINGDATE
END
WHEN #ENDINGDATE <> '' AND #STARTINGDATE = ''
SELECT * FROM SALESABLE
WHERE SALESDATE <= #ENDINGDATE
END
what if u use IF ELSE Statement?
IF #STARTINGDATE IS NOT NULL AND #ENDINGDATE IS NULL
SELECT * FROM SALESTABLE WHERE SALESDATE >= CONVERT(DATE,#STARTINGDATE,103)
ELSE IF #STARTINGDATE IS NOT NULL AND #ENDINGDATE IS NOT NULL
SELECT * FROM SALESTABLE WHERE SALESDATE >= CONVERT(DATE,#STARTINGDATE,103)
AND SALESDATE<DATEADD(DAY,1,CONVERT(DATE,#ENDINGDATE,103))
ELSE
SELECT * FROM SALESTABLE
References http://sqlfiddle.com/#!3/783c03/12
You can try this if StartingDate and EnddingDate dates are string. Using StartingDate and EnddingDate as text you will loose the date picker functionality in SSRS. Anyway here is a method to shorten the above script
Select * FROM SalesTable
Where SalesDate between
CASE WHEN #StartingDate = '' THEN SalesDate ELSE #StartingDate END
AND CASE WHEN #EndingDate = '' THEN SalesDate ELSE #EndingDate END
Method 2
If StartingDate and Endding date are dateTime. Use this method:
FYI: SSRS doesn't allow Blank values for DateTime parameters. It only allows NULL.
First create two parameters StartingDate and EndingDate and check the Allow NULL value.
Your Dataset query will be something like this
SELECT * FROM SalesTable
Where (SalesDate IS NULL OR #StartingDate IS NULL)
OR (SalesDate between ISNULL(#StartingDate, SalesDate)
AND ISNULL(#EndingDate, SalesDate))
Assign you dataset parameters to report parameters
Now your are good to go.

Compare dates regardless of time(MSSQL)

I Used this Store procedure to return data from special date . my QueueDate type is datetime , but when i want use = in Where Clause it return 0 , i want all field that are in one day independence in time of field.
ALTER PROCEDURE [dbo].[SP_GET_QUEUESINFO_BY_DATE]
AS
BEGIN
declare #date2 datetime
set #date2= '2012-09-21'
select COUNT(QueueID) ,
sum(case when QueueNumIn != 0 THEN 1 else 0 end) as 'InQueue',
sum(case when QueueNumOut != 0 THEN 1 else 0 end) as 'OutQueue'
from Queue where QueueDate >= #date2 -- QueueDate = #date2
END
You can CAST the column name to change the datatype from DATETIME to DATE (prior to version 2008+). Try,
ALTER PROCEDURE [dbo].[SP_GET_QUEUESINFO_BY_DATE]
AS
BEGIN
declare #date2 datetime
set #date2= '2012-09-21'
select COUNT(QueueID) ,
sum(case when QueueNumIn != 0 THEN 1 else 0 end) as 'InQueue',
sum(case when QueueNumOut != 0 THEN 1 else 0 end) as 'OutQueue'
from Queue
where CAST(QueueDate as DATE) >= #date2 -- QueueDate = #date2
END
SQL Server, correct?
To compare just the date component of a DateTime value and your are using SQL Server 2008 or higher, you can, as noted cast/convert to a Date datatype. Something like this should do you:
select *
from foo
where convert(date,foo.someDateTimeColumn) = '2012-10-27'
For any version of SQL Server the following will work:
select *
from foo
where convert(datetime,convert(varchar,foo.someDateTimeColumn,112),112) = '2012-10-27'
The above, using the 112 style, converts the datetime value to a compact form ISO 8601 date string 'yyyymmdd' and then converts that back to a datetime value.
Alternatively, for any version of SQL Server, you can test against a time period:
declare
#dtNow datetime
set #dtNow = current_timestamp
declare
#TodayStartOfDay datetime ,
#TodayEndOfDay datetime
set #TodayStartOfDay = convert(datetime,convert(varchar,#dtNow,112),112)
set #TodayEndOfDay = dateadd(ms,-3,datetime(day,1,#dtFrom))
...
select *
from foo
where foo.someDateTimeColumn between #TodayStartOfDay and #TodayEndOfDay
The advantage of this approach is that any indices on your DateTime column are eligible for use by the query optimizer.
WHERE DATEDIFF(DAY,#date2, QueueDate)=0

What's wrong with this SQL query?

SELECT [travel], [fro_m], [t_o], [dep], [arr], [fare], [discount], [faresleeper],
[rating], [seats], [s_no],
[booking_closed] =
CASE WHEN s1from <= #date AND s1to >= #date THEN s1Rate ELSE fare END
WHEN s2from <= #date AND s2to >= #date THEN s2Rate ELSE fare END
WHEN s3rate <= #date AND s3to >= #date THEN s3Rate ELSE fare END
FROM a1_volvo WHERE (fro_m = #fro_m) AND (t_o = #t_o)
The case statement is incorrect:
CASE WHEN s1from <= #date AND s1to >= #date THEN s1Rate
WHEN s2from <= #date AND s2to >= #date THEN s2Rate
WHEN s3rate <= #date AND s3to >= #date THEN s3Rate
else fare END
You have and else statement after each line in the case statement. It should only be at the end.
MSDN Case Statement
In the third WHEN option you are comparing a field with name s3rate with a date .Well I dont know what these fields are but it seems to me that is not a date field...
Also have a look at Kevin & BobTodd answers for another catch...
CASE WHEN s1from <= #date AND s1to >= #date THEN s1Rate
WHEN s2from <= #date AND s2to >= #date THEN s2Rate
WHEN s3rate <= #date AND s3to >= #date THEN s3Rate
ELSE fare
END
is better?
This does the same as his code, but uses between and. They advantage of using it is, that it is better readabl. When doing code review you spare endless time compare whether two name are typed identical
CASE WHEN #date between s1from AND s1to THEN s1Rate
WHEN #date between s2from AND s2to THEN s2Rate
WHEN #date between s3rate AND s3to THEN s3Rate
ELSE fare
END