What's wrong with this SQL query? - sql

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

Related

Get the Date Difference Between two dates excluding fridays in SQL Server

I have the following two dates :
startDate = '2017-04-04' and endDate = '2017-04-12'
I would like to find the total count of days between these two dates excluding 'Fridays' using SQL Server.
Any Help from the SQL Server Experts will be highly appreciated!! Thanks in Advance!!
You can use DATENAME to check for 'Friday' do this, something like this :
DECLARE #startDate DATETIME
DECLARE #endDate DATETIME
SET #startDate = '2017-04-04'
SET #endDate = '2017-04-12'
Declare #count int
set #count=0
while #startDate < #endDate
Begin
IF DATENAME(dw, #startDate) <> 'Friday'
SET #count = #count + 1
SET #startDate = DateAdd(d, 1, #startDate)
END
select #count
This will result in :
7
just add below clause to your query
select count(*) from table
where startDate >= '2017-01-12' and endDate <= '2017-04-27'
and datename(WEEKDAY,startdate))<>'Friday'

How to compare time in 24 hours format in sql server

I want to compare two time values with current time in sql server. The time is in 24 hours format. The code I am trying is,
declare #StartTime varchar(10)='16:30'
declare #EndTime varchar(10)='10:10'
declare #CurrTime varchar(10)='09:30'
select case when CONVERT(time,#CurrTime) between CONVERT(time,#StartTime) and CONVERT(time,#EndTime) then 'SUCCESS' else 'FAIL' end
and gives the output as 'FAIL'.
Please suggest solution.
You need to use datetime datatype and CASE WHEN to check if start date > end date to apply DATEADD to star date in case it is bigger then end date:
DECLARE #Start nvarchar(5)= N'16:30',
#End nvarchar(5)= N'10:10',
#Curr nvarchar(5)=N'09:30'
DECLARE #curdate nvarchar(9) = CONVERT(nvarchar(10),GETDATE(),112)+' '
DECLARE #starttime datetime = #curdate + #Start + ':00.000',
#endtime datetime = #curdate + #End + ':00.000',
#currtime datetime = #curdate + #Curr + ':00.000'
SELECT CASE WHEN #currtime between (
CASE WHEN #starttime > #endtime
THEN DATEADD(day,-1,#starttime)
ELSE #starttime END
) and #endtime THEN 'SUCCESS'
ELSE 'FAIL' END as result

How can I include null values in BETWEEN clause?

In my query's where clause I have the condition:
User.DateOfBirth BETWEEN #startDate AND #endDate
Where #startDate and #endDate are nullable. If #startDate is null, I want all values less than or equal to #endDate; if #endDate is null, I want all values greater than or equal to #startDate; if both are null I want all values.
My failed attempt - returns 0 results
((( User.DateOfBirth > #startDate) OR (#startDate Is null)) AND (( User.DateOfBirth < #endDate) OR (#endDate is null)) )
(Editor's note: BETWEEN includes the end points (less/greater than or equal), but the description for the null cases didn't. I've assumed this is an oversight.
Try this:
[User].DateOfBirth BETWEEN ISNULL(#startDate,[User].DateOfBirth) AND ISNULL(#endDate,[User].DateOfBirth)
Two approaches spring to mind:
Tread the four cases separately and then OR them together:
start and end are null: any date matches,
start is null, so need DoB <= end
send is null, so need DoB >= start
neither is null, so need between
This will lead to a long expression.
Use IsNull:
As shown by mehdi lotfi in his answer.
If you are using a nullable datetime type you could use
User.DateOfBirth BETWEEN isnull(#startDAte, CAST('1753-01-01' AS datetime)) AND isnull(#endDAte, CAST('9999-12-31' AS datetime))
for datetime2 use
User.DateOfBirth BETWEEN isnull(#startDAte, CAST('0001-01-01' AS datetime2)) AND isnull(#endDAte, CAST('9999-12-31' AS datetime2))
WHERE
((User.DateOfBirth BETWEEN #startDAte AND #endDAte) AND #startDAte is not null AND #endDAte is not null)
OR
((User.DateOfBirth =< #endDAte) AND #startDAte is null AND #endDAte is not null)
OR
((User.DateOfBirth >= #startDAte) AND #startDAte is not null AND #endDAte is null)
OR
(1=1 AND #startDAte is null AND #endDAte is null)
Try this:
select * from yourtable
where
(#startdate is null and #enddate is null)
or
(#startdate is null and #enddate is not null and User.DateOfBirth <= #enddate)
or
(#startdate is not null and #enddate is null and User.DateOfBirth >= #startdate)
or
(#startdate is not null and #enddate is not null and User.DateOfBirth BETWEEN #startdate AND #enddate)
This addresses all possible cases:
Both parameters are null - return all results
Non null end date only - return all results where DOB <= end date
Non null start date only - return all results where DOB >= start date
Both parameters are non null - return all results where DOB is between start and end dates

My SQL if statement is not reading the value i pass it

Hi i am relatively new to SQL so this is probably a basic question. i am trying to pass a variable and use that variables content to determine what it runs. The variable is a char(2) and it can have the following possible values:
Accepted
= AP
All
= AL
Declined
= DE
Line Manager Accepted
= LA
Admin Accepted
= AA
here is what it should do. I should input a start and an end date and then the two digit code to decide which holiday bookings i select. in the example below i am looking for a date range between the 2 holidays with any status (accepted or declined):
EXEC spSearchHoliday '2013-04-01','2015-05-24','PE'
This returns the correct result! But if i try to run a date search between two dates with any other status it continues to return every date. in the example below i want only to return accepted holidays:
EXEC spSearchHoliday '2013-04-01','2015-05-24','AP'
But instead it still returns every status holiday between the range.
Below is the stored procedure itself the long holidays.startdate and holidays.EndDate sections work fine its jsut the status related parts that are not working correctly.
AS
BEGIN
IF #Status = 'AL'
BEGIN
SELECT StartDate,
EndDate,
Duration,
[Status]
FROM Holidays
WHERE Holidays.Startdate <= #Startdate AND Holidays.Enddate >= #EndDate
OR Holidays.Startdate >= #Startdate AND Holidays.Enddate <= #EndDate
OR Holidays.Startdate <= #Enddate AND Holidays.Enddate >= #EndDate
OR Holidays.Startdate <= #Startdate AND Holidays.Enddate >= #Startdate
END
IF #Status <> 'AL'
BEGIN
SELECT StartDate,
EndDate,
Duration,
[Status]
FROM Holidays
WHERE Holidays.Status = #Status
AND Holidays.Startdate <= #Startdate AND Holidays.Enddate >= #EndDate
OR Holidays.Startdate >= #Startdate AND Holidays.Enddate <= #EndDate
OR Holidays.Startdate <= #Enddate AND Holidays.Enddate >= #EndDate
OR Holidays.Startdate <= #Startdate AND Holidays.Enddate >= #Startdate
END
END
Perhaps the problem is incorrect priority in the second query. Please try using the following WHERE-clause in your second query:
WHERE Holidays.Status = #Status
AND (Holidays.Startdate <= #Startdate AND Holidays.Enddate >= #EndDate
OR Holidays.Startdate >= #Startdate AND Holidays.Enddate <= #EndDate
OR Holidays.Startdate <= #Enddate AND Holidays.Enddate >= #EndDate
OR Holidays.Startdate <= #Startdate AND Holidays.Enddate >= #Startdate)

get DATEDIFF excluding weekends using sql server

I am using this query to get time taken.
SELECT DATEDIFF(dd, ActualStartDate, ActualCompletionDate) AS TimeTaken
FROM TableName
Now I want to exclude weekends and only include Mon-Fri as days counted.
Example query below, here are some details on how I solved it.
Using DATEDIFF(WK, ...) will give us the number of weeks between the 2 dates. SQL Server evaluates this as a difference between week numbers rather than based on the number of days. This is perfect, since we can use this to determine how many weekends passed between the dates.
So we can multiple that value by 2 to get the number of weekend days that occurred and subtract that from the DATEDIFF(dd, ...) to get the number of weekdays.
This doesn't behave 100% correctly when the start or end date falls on Sunday, though. So I added in some case logic at the end of the calculation to handle those instances.
You may also want to consider whether or not the DATEDIFF should be fully inclusive. e.g. Is the difference between 9/10 and 9/11 1 day or 2 days? If the latter, you'll want to add 1 to the final product.
declare #d1 datetime, #d2 datetime
select #d1 = '9/9/2011', #d2 = '9/18/2011'
select datediff(dd, #d1, #d2) - (datediff(wk, #d1, #d2) * 2) -
case when datepart(dw, #d1) = 1 then 1 else 0 end +
case when datepart(dw, #d2) = 1 then 1 else 0 end
I found when i used this there was a problem when d1 fell on saturday. Below is what i used to correct this.
declare #d1 datetime, #d2 datetime
select #d1 = '11/19/2011' , #d2 = '11/28/2011'
select datediff(dd, #d1, #d2) +case when datepart(dw, #d1) = 7 then 1 else 0 end - (datediff(wk, #d1, #d2) * 2) -
case when datepart(dw, #d1) = 1 then 1 else 0 end +
case when datepart(dw, #d2) = 1 then 1 else 0 end
BEGIN
DECLARE #totaldays INT;
DECLARE #weekenddays INT;
SET #totaldays = DATEDIFF(DAY, #startDate, #endDate)
SET #weekenddays = ((DATEDIFF(WEEK, #startDate, #endDate) * 2) + -- get the number of weekend days in between
CASE WHEN DATEPART(WEEKDAY, #startDate) = 1 THEN 1 ELSE 0 END + -- if selection was Sunday, won't add to weekends
CASE WHEN DATEPART(WEEKDAY, #endDate) = 6 THEN 1 ELSE 0 END) -- if selection was Saturday, won't add to weekends
Return (#totaldays - #weekenddays)
END
This is on SQL Server 2014
declare #d1 datetime, #d2 datetime
select #d1 = '4/19/2017', #d2 = '5/7/2017'
DECLARE #Counter int = datediff(DAY,#d1 ,#d2 )
DECLARE #C int = 0
DECLARE #SUM int = 0
WHILE #Counter > 0
begin
SET #SUM = #SUM + IIF(DATENAME(dw,
DATEADD(day,#c,#d1))IN('Sunday','Monday','Tuesday','Wednesday','Thursday')
,1,0)
SET #Counter = #Counter - 1
set #c = #c +1
end
select #Sum
If you hate CASE statements as much as I do, and want to be able to use the solution inline in your queries, just get the difference of days and subtract the count of weekend days and you'll have the desired result:
declare #d1 datetime, #d2 datetime, #days int
select #d1 = '2018/10/01', #d2 = '2018/11/01'
SET #days = DateDiff(dd, #d1, #d2) - DateDiff(ww, #d1, #d2)*2
print #days
(The only caveat--or at least point to keep in mind--is that this calculation is not inclusive of the last date, so you might need to add one day to the end date to achieve an inclusive result)
I just want to share the code I created that might help you.
DECLARE #MyCounter int = 0, #TempDate datetime, #EndDate datetime;
SET #TempDate = DATEADD(d,1,'2017-5-27')
SET #EndDate = '2017-6-3'
WHILE #TempDate <= #EndDate
BEGIN
IF DATENAME(DW,#TempDate) = 'Sunday' OR DATENAME(DW,#TempDate) = 'Saturday'
SET #MyCounter = #MyCounter
ELSE IF #TempDate not in ('2017-1-1', '2017-1-16', '2017-2-20', '2017-5-29', '2017-7-4', '2017-9-4', '2017-10-9', '2017-11-11', '2017-12-25')
SET #MyCounter = #MyCounter + 1
SET #TempDate = DATEADD(d,1,#TempDate)
CONTINUE
END
PRINT #MyCounter
PRINT #TempDate
If you do have a holiday table, you can also use that so that you don't have to list all the holidays in the ELSE IF section of the code. You can also create a function for this code and use the function whenever you need it in your query.
I hope this might help too.
Using https://stackoverflow.com/a/1804095 and JeffFisher30's answer above (https://stackoverflow.com/a/14572370/6147425) and my own need to have fractional days, I wrote this:
DateDiff(second,Start_Time,End_Time)/86400.0
-2*DateDiff(week, Start_Time, End_Time)
-Case When (DatePart(weekday, Start_Time)+##DateFirst)%7 = 1 Then 1 Else 0 End
+Case When (DatePart(weekday, End_Time)+##DateFirst)%7 = 1 Then 1 Else 0 End
Use this function to calculate the number of business days excluding Saturday and Sunday. Also it will exclude start date and it will include end date.
-- Select [dbo].[GetBussinessDays] ('02/18/2021', '03/06/2021') -- 11 days
CREATE or ALTER FUNCTION [dbo].[GetBussinessDays] (
#StartDate DATETIME,
#EndDate DATETIME
)
returns INT AS
BEGIN
DECLARE #tempStartDate DATETIME= #StartDate;
DECLARE #tempEndDate DATETIME = #EndDate;
IF(#tempStartDate IS NULL
OR
#tempEndDate IS NULL)
BEGIN
RETURN NULL;
END
--To avoid negative values reverse the date if StartDate is grater than EndDate
IF(#StartDate > #EndDate)
BEGIN
SET #StartDate = #tempEndDate;
SET #EndDate = #tempStartDate;
END
DECLARE #Counter INT = Datediff(day,#StartDate ,#EndDate);
DECLARE #TempCounter INT = 0;
DECLARE #TotalBusinessDays INT = 0;
WHILE #Counter >= 0
BEGIN
IF(#TempCounter > 0 OR #Counter = 1) -- To ignore first day's calculation
Begin
SET #TotalBusinessDays = #TotalBusinessDays + Iif(Datename(dw, Dateadd(day,#TempCounter,#StartDate)) IN('Monday',
'Tuesday',
'Wednesday',
'Thursday',
'Friday'),1,0)
END
SET #Counter = #Counter - 1
SET #TempCounter = #TempCounter +1
END
RETURN #TotalBusinessDays;
END
Using #Derek Kromm answer (Current Marked Answer)
I have modified so it IS tolerant of any localisations that may be on the target server.
DECLARE #d1 DATETIME, #d2 DATETIME
SELECT #d1 = '10/01/2022', #d2 = '10/28/2022'
SELECT (datediff(dd, #d1, #EndQuery)+1) - (datediff(wk, #d1, dateadd(dd,1,#d2)) * 2)
- CASE WHEN DateName(WEEKDAY, #d1) = 'Sunday' THEN 1 ELSE 0 END -- This includes for start date edge case
+ CASE WHEN DateName(WEEKDAY, #d2) = 'Saturday' THEN 1 ELSE 0 END -- This includes for end date edge case.
This is with the end date being innclusive.
/*
EXAMPLE:
/MONDAY/
SET DATEFIRST 1
SELECT dbo.FUNC_GETDATEDIFFERENCE_WO_WEEKEND('2019-02-01','2019-02-12')
*/
CREATE FUNCTION FUNC_GETDATEDIFFERENCE_WO_WEEKEND
(
#pdtmaLastLoanPayDate DATETIME,
#pdtmaDisbursedDate DATETIME
)
RETURNS BIGINT
BEGIN
DECLARE
#mintDaysDifference BIGINT
SET #mintDaysDifference = 0
WHILE CONVERT(NCHAR(10),#pdtmaLastLoanPayDate,121) <= CONVERT(NCHAR(10),#pdtmaDisbursedDate,121)
BEGIN
IF DATEPART(WEEKDAY,#pdtmaLastLoanPayDate) NOT IN (6,7)
BEGIN
SET #mintDaysDifference = #mintDaysDifference + 1
END
SET #pdtmaLastLoanPayDate = DATEADD(DAY,1,#pdtmaLastLoanPayDate)
END
RETURN ISNULL(#mintDaysDifference,0)
END