Is there an easier way to set a DateTime in SQL Server to be 23:59:59 - sql

Good Morning All,
I'm trying to refactor an SQL stored procedure. I'm no SQL expert, but something tells me there must be a better way to do this.
IF #ipv_dtEndDate IS NOT NULL
BEGIN
SET #ipv_dtEndDate = DATEADD(hh,23,#ipv_dtEndDate)
SET #ipv_dtEndDate = DATEADD(mi,59,#ipv_dtEndDate)
SET #ipv_dtEndDate = DATEADD(ss,59,#ipv_dtEndDate)
END
This value is used later inside a WHERE clause. These filters seem difficult to understand to me. I was hoping to come up with a cleaner implementation.
AND qtrh.StatusTime <= IsNull(#ipv_dtEndDate, qtrh.StatusTime)
And this date calculation...
AND DATEDIFF(ss,qtrh.StatusTime,ISNULL(#dtNow,DATEADD(ss,-1,qtrh.StatusTime))) < DATEDIFF(ss,ISNULL(#dtDateOptionCompare,GETDATE()),GETDATE())
... seems quite convoluted and unreadable. If any SQL gurus out there have some suggestions on how I can improve this, I would love to hear some ideas. Thanks for your time. Have a terrific holiday weekend.
Cheers,
~ck in San Diego

If the only use of #ipv_dtEndDate is inside the Where clause, you could remove the entire IF #ipv_dtEndDate IS NOT NULL block, and replace the condition in the SQL query with:
AND qtrh.StatusTime < DATEADD(dd,1,IsNull(#ipv_dtEndDate, qtrh.StatusTime))
(Strictly speaking, you will now also be including StatusTime values between 23:59:59 and 00:00:00, which were previously excluded.)

How about this?
SET #ipv_dtEndDate = CONVERT(varchar, #ipv_dtEndDate, 101) + ' 23:59:59'

Usually I use < and the date for the next day rather than trying to run a <+ with the last second of midnight.

You could convert the date to varchar, add your "23:59:59" and then convert it back to datetime

If you feel uncomfortable with the varchar approach, you can do the following.
SET #ipv_dtEndDate = DATEADD(ss, DATEDIFF(ss, 0, '11:59:59'), #ipv_dtEndDate)

To convert a date without a time (rather, with the time set to "midnight the morning of") to the "end of the day",
you can just add the number of seconds:
DECLARE #ipv_dtEndDate datetime
SET #ipv_dtEndDate = 'Sep 3, 2010'
PRINT convert(varchar(50), #ipv_dtEndDate, 109) -- Before
SET #ipv_dtEndDate = dateadd(ss, 1439, #ipv_dtEndDate)
PRINT convert(varchar(50), #ipv_dtEndDate, 109) -- After
Of course, SQL datetime is accurate to the [333rd of a] millisecond, so the end of the day is actually:
DECLARE #ipv_dtEndDate datetime
SET #ipv_dtEndDate = 'Sep 3, 2010'
PRINT convert(varchar(50), #ipv_dtEndDate, 109) -- Before
SET #ipv_dtEndDate = dateadd(ms, 1439997, #ipv_dtEndDate)
PRINT convert(varchar(50), #ipv_dtEndDate, 109) -- After
Using the built in (and mathematically based) date/time functions will be more efficient than converting to character strings and back.

This will return the latest time for today:
SELECT DATEADD(ms, -2, DATEADD(dd, 1, DATEDIFF(dd, 0, GETDATE())))
Just substitute GETDATE() with whatever you want to be inclusive. So, in your example:
AND qtrh.StatusTime <= DATEADD(ms, -2, DATEADD(dd, 1, DATEDIFF(dd, 0, #ipv_dtEndDate)))

Related

Is there a way to preserve locale when format a datetime in SQL? [duplicate]

I have an sql DateTime (ms sql server) and want to extract the same date without the seconds:
e.g. 2011-11-22 12:14:58.000 to become: 2011-11-22 12:14:00.000
How can I do this? I was thinking to use DATEADD in combination with DATEPART but seems very error prone (besides performance issues)
SELECT DATEADD(MINUTE, DATEDIFF(MINUTE, 0, yourcolumn), 0) FROM yourtable
This will be effective, if you don't want a slow conversion between datatypes.
For a solution that truncates using strings try this:
SELECT CAST(CONVERT(CHAR(16), GetDate(),20) AS datetime)
CHAR(16) works only if our variable is converted to ODBC canonical format, as shown above by using 20 as the format specifier.
DECLARE #date DateTime = '2011 Nov 22 12:14:55';
SELECT CONVERT(Char(16), #date ,20) AS datetime
Results:
| datetime |
|------------------|
| 2011-11-22 12:14 |
Then you simply cast back to a DateTime type to continue using the value.
NOTE: This is only viable for data types that do not carry TimeZone info.
Also type conversions to VarChar and back are usually LESS performant than using DateTime functions that use numeric operations internally.
Consider other solutions posted if performance is a concern or if you must retain timezone information.
DECLARE #TheDate DATETIME
SET #TheDate = '2011-11-22 12:14:58.000'
DATEADD(mi, DATEDIFF(mi, 0, #TheDate), 0)
In queries
/* ...all records in that minute; index-friendly expression */
WHERE TheDate BETWEEN DATEADD(mi, DATEDIFF(mi, 0, #TheDate), 0)
AND DATEADD(mi, DATEDIFF(mi, 0, #TheDate) + 1, 0)
Date and time needs carefully and not being converted as TEXT.
My personal solution:
CREATE FUNCTION [dbo].[fnDateTimeTruncated]
(
#datetime DATETIME
)
RETURNS DATETIME
AS
BEGIN
RETURN DATETIMEFROMPARTS ( year(#datetime), month(#datetime), day(#datetime), DATEPART(hh,#datetime), DATEPART(mi,#datetime), 0, 0)
END
Edited:
Regarding http://blog.waynesheffield.com/wayne/archive/2012/03/truncate-a-date-time-to-different-part/, DateAdd has a better performance.
Thanks to t-clausen.dk
With a little fiddling around, this seems to work well:
SELECT CAST(CONVERT(CHAR(17), bl.[time],113) AS varchar(17))
Result given: 2011-11-22 12:14
The exact way I'm using it in my query as part of the selection list :
,CAST(CONVERT(CHAR(17), bl.[time],113) AS varchar(17))
+ ' (UTC +0)' AS [TIME]
Gives me the result: 15 Dec 2017 06:43 (UTC +0)
From SQL Server 2014, You can use Format function for this.
for Ex.
declare #Startdate datetime = '2020-11-07 15:27:50.713'
set #Startdate = Convert(datetime,FORMAT(#Startdate, 'yyyy-MM-dd HH:mm'))
> Result is
2020-11-07 15:27:00.000
If there is no milliseconds, than
DECLARE #dt datetime2 = '2011-11-22 12:14:58.000';
DECLARE #goalDt datetime2 = DATEADD(second,-DATEPART(second,#dt), #dt);
To remove a milliseconds part, add
SET #goalDt = DATEADD(millisecond,-DATEPART(millisecond,#goalDt ), goalDt dt);
To Round Off it:
DECLARE #TheDate DATETIME;
SET #TheDate = '2019-1-2 12:14:58.400';
SELECT CAST(#TheDate AS SMALLDATETIME);
To just Truncate:
DECLARE #TruncTheDate DATETIME;
SET #TruncTheDate = '2019-1-2 12:14:58.400';
SELECT DATEADD(mi, DATEDIFF(mi, 0, #TruncTheDate), 0);
select substring(cast(cast(getdate() as time(0)) as char(8)),0,6)

Standard syntax for previous Monday/Sunday dates using GETDATE()

These seem a bit verbose and maybe over-complicated; can I simplify and/or make them more readable?
They are returning the dates (as integer type) of the previous week's Monday and Sunday.
declare #sDate int = CONVERT(CHAR(8),DATEADD(wk, DATEDIFF(wk,0,GETDATE())-1, 0),112),
#edate int = CONVERT(CHAR(8),DATEADD(wk, DATEDIFF(wk,0,GETDATE()), -1),112);
From my experience, if you convert a date to char for any reason other than presentation, you have made a mistake. Also - if you call GetDate twice, you should expect different answers.
DECLARE
#Now datetime,
#WeekStart datetime,
#LastWeekStart datetime,
#LastWeekEnd datetime
SET #Now = GetDate()
SET #WeekStart = DateAdd(wk, DateDiff(wk, 0, #Now), 0) --standard time trimmer
SET #LastWeekStart = DateAdd(wk, -1, #WeekStart)
SET #LastWeekEnd = DateAdd(dd, -1, #WeekStart)
SELECT #Now, #WeekStart, #LastWeekStart, #LastWeekEnd
Also be aware that there is a sql setting that controls where sql server thinks that the week starts, so this code may not give Monday->Sunday depending on that setting.
The simplest way is to use a calendar table. A calendar table might simplify your query to something along these lines.
-- Select the previous Sunday. The right inequality operator depends
-- on exactly what *you* mean by "previous".
select max(calendar_date)
from calendar
where calendar_date < current_date
and calendar_day_of_week = 'Sun';

SQL HELP... CONVERT(int, CONVERT(datetime, FLOOR(CONVERT(float, getdate())))

I am having a problem adjusting this part of my SQL statement:
HAVING dbo.BOOKINGS.BOOKED = CONVERT(int, CONVERT(datetime,
FLOOR(CONVERT(float, GETDATE()))) + 2)
Normally, the page that uses this statement just lists the amount of sales for today, I want to switch the GETDATE() to a date that I declare. I tried all different formats and none have worked
Use the DATEADD/DATEDIFF method of setting the time portion to midnight of the current date - it's the fastest means, and casting to FLOAT can be unreliable:
HAVING BOOKINGS.dbo.BOOKED = CONVERT(INT, DATEADD(dd, DATEDIFF(dd, 0, GETDATE()), 0))+2
Then, you can set your own date easily if you use a variable (#var in this example, within a stored procedure or function):
DECLARE #var DATETIME
SELECT ...
HAVING BOOKINGS.dbo.BOOKED = CONVERT(INT, DATEADD(dd, DATEDIFF(dd, 0, #var), 0))+2
This assumes #var is a DATETIME data type. Otherwise, you'll need to use a date format SQL Server will implicitly convert to a DATETIME -- or use CAST/CONVERT to explicitly convert the value.
if you want you to give your own date you could do this instead of getdate() which gives current system timestamp.
Cast('2010-11-04 13:28:00.000' as datetime)
How about
declare #myDate as datetime
set #myDate = '11/2/2010'
. . .
HAVING dbo.BOOKINGS.BOOKED = CONVERT(int, CONVERT(datetime,
FLOOR(CONVERT(float, #myDate ))) + 2)
That should do it, and it should automatically do the type conversion on your date string used in the set statement, or you could just pass in a datetime parameter if this is in a stored procedure.

Fastest way to check date range

I store events in SQLServer 2005 where the time the event occured is important and must be stored in the datebase. What is the fastest way to write the date range check in the where clause to ensure everything on that day is selected?
Currently when #DateStart and #DateEnd are passed in I set #DateStart to midnight and set #DateEnd to the last instant before midnight as the very first thing to catch every possible event on the day.
IF (#DateStart IS NOT NULL)
BEGIN
SET #DateStart = CAST (
( CAST (DATEPART (yyyy,#DateStart) AS NVARCHAR(4)) +'/'+
CAST (DATEPART (mm,#DateStart) AS NVARCHAR(2)) +'/'+
CAST (DATEPART (dd,#DateStart) AS NVARCHAR(2)) +' '+
'00:00:00.000'
)
AS DATETIME)
END
IF (#DateEnd IS NOT NULL)
BEGIN
SET #DateEnd = CAST (
( CAST (DATEPART (yyyy,#DateEnd) AS NVARCHAR(4)) +'/'+
CAST (DATEPART (mm,#DateEnd) AS NVARCHAR(2)) +'/'+
CAST (DATEPART (dd,#DateEnd) AS NVARCHAR(2)) +' '+
'23:59:59.997'
)
AS DATETIME
)
END
So the where clause is very easy to read:
WHERE ( EventDate >= #DateStart AND EventDate <= #DateEnd )
Thanks,
You could always use the alternate syntax of WHERE EventDate BETWEEN #DateStart AND #DateEnd
Your where clause would look like;
WHERE DateCol >= DATEADD(dd, DATEDIFF(dd, 0, #DateStart), 0) --Midnight on the Start date
AND DateCol < DATEADD(dd, DATEDIFF(dd, 0, #DateEnd + 1), 0) --Midnight of the day after End date
and all your IF statement would do is handle null parameters (i.e. IF #DateEnd IS NULL THEN SET #DateEnd = #DateStart)
You probably want to Index on DATEADD(dd, DATEDIFF(dd, 0, DateCol), 0) if your table is large.
the fastest way to truncate a date, previous midnight:
DATEADD(day, DATEDIFF(day, '19010101', LastModifiedDate), '19010101')
next midnight:
DATEADD(day, DATEDIFF(day, '19010101', LastModifiedDate)+1, '19010101')
You can also wrap this up as an inline UDF:
http://sqlblog.com/blogs/alexander_kuznetsov/archive/2008/05/23/reuse-your-code-with-cross-apply.aspx
Try this:
WHERE DATEPART(yyyy, EventDate) = DATEPART(yyyy, getdate())
AND DATEPART(dy, EventDate) = DATEPART(dy, getdate()) --day of year
EDIT To address Tom H's comment:
I've never had any luck with indexes on date fields; what has always worked better for me was extra integer columns to handle the year and day of year values and indexed those instead.
AlexK's is probably the best idea all around.
The only thing I would worry about is performance of using functions in the predicate.
You should consider adding a column called searchdate, or something similar, to your table to contain the date without the time. I'd also suggest indexing this column, especially if you're going to be searching against the column's data a lot.
When you query on this column you will not have any scalar functions in your SQL to rob your indexes of their performance.
The con... well, additional storage space, time during insert to write the data (not much here though).
SQL Server 2008 has better support for date only data types. You could also check in on it.
I think this T-SQL is equivalent to the code you have:
-- set time portion of #DateStart back to midnight
SET #DateStart = CONVERT(DATETIME,CONVERT(VARCHAR(10),#DateStart,20),20)
-- advance time portion of #DateEnd to last instant before next midnight
SET #DateEnd = CONVERT(DATETIME,CONVERT(VARCHAR(11),#DateEnd,20)+'23:59:59.997',21)
The CONVERT function will handle NULLS, so there's no need for a separate test for a NULL value (unless, of course, you are doing some special handling other than what you are showing, and are not passing the NULL values through to query predicate (i.e. WHERE clause). Or, perhaps you are expecting a lot of the arguments to be NULL, and you want to avoid the overhead of the calls to CONVERT.
However, I concur with Tom H.'s recommendation, and avoid messing with subtracting milliseconds, and instead set the #DateEnd to midnight of the following day e.g.
-- advance #DateEnd to midnight of following day
SET #DateEnd = DATEADD(day,1,CONVERT(DATETIME,CONVERT(VARCHAR(10),#DateEnd,20),20))
and change the predicate to do a range test like this:
WHERE (EventDate >= #DateStart AND EventDate < #DateEnd)
You can avoid the separate SET statements, and move the expressions straight into the query, but I don't expect that will improve performance any, and make the SQL statement harder to read, you'd definitely want to keep the comments ...
WHERE (EventDate >= CONVERT(DATETIME,CONVERT(VARCHAR(10),#DateStart,20),20)
AND EventDate

Comparing effective dates in SQL

Wondering if there is a better why in the WHERE clause of choosing records when you need to look at effective start and end dates?
Currently this how I've done it in the past on MS SQL Server. Just worried about the date and not the time. I'm using SQL Server 2005.
AND Convert(datetime, Convert(char(10), ep.EffectiveStartDate, 101))
<= Convert(datetime, Convert(char(10), GetDate(), 101))
AND Convert(datetime, Convert(char(10), ep.EffectiveEndDate, 101))
>= Convert(datetime, Convert(char(10), GetDate(), 101))
That is terrible, take a look at Only In A Database Can You Get 1000% + Improvement By Changing A Few Lines Of Code to see how you can optimize this since that is not sargable
Also check out Get Datetime Without Time and Query Optimizations With Dates
#Darren Kopp - you can use
set #date2 = '20201001'
this will let you lose the cast.
footndale - you can use date arithmetic to remove the time as well. Something like
select dateadd(d, datediff(d, 0, CURRENT_TIMESTAMP), 0)
to get today's date (without the time). I believe this is more efficient than casting back and forth.
#Darren Kopp
Be carefull with BETWEEN, check out How Does Between Work With Dates In SQL Server?
AND DateDiff(Day, 0, GetDate()) + 1 > ep.EffectiveStartDate
AND DateDiff(Day, 0, GetDate()) < ep.EffectiveEndDate
I think you will find that these conditions offer the best performance possible. This will happily utilize indexes.
I am also very sure that this is right and will give the right data. No further calculation of dates without time portions is needed.
try
ep.EffectiveStartDate BETWEEN #date1 AND #date2
where you would do something like
declare #date1 datetime, #date2 datetime;
set #date1 = cast('10/1/2000' as datetime)
set #date2 = cast('10/1/2020' as datetime)