SQL query Performance when using Datetime in where / On clause - sql

I have the 5l record in table. need to query...Which is one is query fast,Input parameter #RegistrationFrom DATE,#RegistrationTo DATE
Approach #1:
WHERE
CAST(Act.RegistrationOn AS DATE) BETWEEN CAST(#RegistrationFrom AS DATE)
AND CAST(#RegistrationTo AS DATE)
Approach #2 - convert into datetime:
DECLARE #From DATETIME, #Todate DATETIME;
SELECT #From = #RegistrationFrom;
SELECT #Todate = DATEADD(day, 1, #RegistrationTo);
WHERE
Act.RegistrationOn BETWEEN #From AND #Todate
Approach #3:
WHERE CONVERT(VARCHAR, Act.RegistrationOn,101) BETWEEN #From AND #Todate
I ma getting fast response approach 3 than above 1,2.
How it is working?

I would write the query as:
SELECT #Todate = DATEADD(day, 1, #RegistrationTo);
. . .
WHERE Act.RegistrationOn >= #From AND
Act.RegistrationOn < #Todate
Whether you use variables or a cast on the variable should have no or little affect on performance. More important considerations are the use of indexes and interpretability (does the code do what you intend).
Aaron Bertrand has a very good blog on why you shouldn't use BETWEEN for dates.
As for the first version . . . it is actually more reasonable than you might think. In general, function calls prevent the use of indexes on columns. However, SQL Server makes an exception for conversion of a datetime to date. So, it will still use an index.
That said, I would still go with the above version.

Try it like this:
WITH MyPrms AS
(
SELECT CAST(#RegistrationFrom AS DATE) AS fromD
,CAST(#RegistrationTo AS DATE) + 1 AS toD
)
SELECT *
FROM MyPrms
CROSS JOIN Act
WHERE Act.Registration>=fromD AND Act.RegistrationOn<toD;
It is fully inlineable (in VIEWS, functions...) and sargable (optimizer can use indexes)
EDIT just for clearity
By adding +1 to your upper border date (which was casted to a timeless DATE before, the upper border is midnight after the last day. By using a < you will get all data from the full day. BETWEEN includes the border which can lead to unexpected errors...

Both options are equivalent from performance point of view:
Approach 1
WHERE Act.RegistrationOn AS DATE BETWEEN CAST(#RegistrationFrom AS DATE) AND CAST(#RegistrationTo AS DATE)
Approach 2
WHERE RegistrationOn BETWEEN #From AND #Todate
Each casting in Approach 1 is executed only once and not for each row as you might expected.
For clarity, I would take option 2 or even better send the parameter with the right type to this query.
Also, an index on RegistrationOn column would definitively help. Please note a CAST on RegistrationOn column has been removed from Approach 1 as this would prevent SQL Server to use this index.

WHERE CONVERT(VARCHAR, Act.RegistrationOn,101) BETWEEN #From AND #Todate

Related

SQL Server 2014. Fastest way to update a numeric field

I can think of this 2 options, but not sure if there is any other one. What will be faster?
DECLARE #dateVar datetime = '20170101';
UPDATE SALESLINE
SET REMAINSALESFINANCIAL= 0
WHERE CONVERT(DATE, CREATEDDATETIME) < CONVERT(DATE, #dateVar)
or
DECLARE #dateVar datetime = '20170101';
UPDATE SALESLINE
SET REMAINSALESFINANCIAL= 0
WHERE CONVERT(DATE, CREATEDDATETIME) < CONVERT(DATE, #dateVar)
AND REMAINSALESFINANCIAL != 0
REMAINSALESFINANCIAL is numeric
I have an index on CREATEDDATETIME
Assuming that REMAINSALESFINANCIAL does not contain any NULLs you can just use
UPDATE SALESLINE
SET REMAINSALESFINANCIAL = 0
WHERE CREATEDDATETIME < CONVERT(DATE, #dateVar)
AND REMAINSALESFINANCIAL <> 0
As CONVERT(DATE, #dateVar) will have a time part of 00:00:00 and so any datetime that is less than this must have a date part of the previous day or earlier.
Casting a datetime column to date doesn't prevent index use but it is more optimal not to.
I agree with the other answers that the second option should be faster but there are additional considerations from a performance perspective.
If the assigned value has a time of midnight, the explicit conversion is unnecessary.
Instead of a local variable, I suggest the query be parameterized so that stats histogram is used instead of average cardinality to better optimize the query. This is automatic if your actual query uses a stored procedure parameter directly or is called as a parameterized query from application code. If parameter sniffing is an issue (unlikely here, IMHO), add an OPTION RECOMPILE query hint.
EXEC sp_executesql N'
UPDATE SALESLINE
SET REMAINSALESFINANCIAL= 0
WHERE
CREATEDDATETIME < #dateVar
AND REMAINSALESFINANCIAL <> 0;'
, N'#dateVar datetime'
, #dateVar = '20180101';
The second version is better. If you have some rows where REMAINSALESFINANCIAL = 0, then the engine will not even try to update those rows, filtering them out earlier in the process. Note: this assumes that REMAINSALESFINANCIAL is never NULL.
In general, having a function on a column in the WHERE is a bad idea. In this case, though, SQL Server will still use an index, so that is not a problem.
It would be interesting to compare the performance with an index on (CREATEDDATETIME) versus (CREATEDDATETIME, REMAINSALESFINANCIAL). In the first case, it has to work harder to find the rows to update. In the second, it has to update the index when it changes the values.

SQL statement between date

This is driving me crazy and not sure what I'm missing here..
so here is my data column looks like:
StartDateTime:
---------------
2012-01-17 11:13:46.530
2012-01-17 11:17:22.530
2012-02-17 11:31:22.223
here is my query trying to get:
select * from tablName
where convert(varchar(10), startDateTime, 101) between '2012-01-17' and '2012-01-17'
based on the above I should be getting TWO rows? but it does not, it return zero rows. what will be the correct way of doing?
PS:
I've looked at the MSDN site too:
Your query would only match dates that are between 2012-01-17 00:00:00 and 2012-01-17 00:00:00. So, the only matches would be when the date is exactly 2012-01-17 00:00:00.
Instead, I would do this:
declare #dateInput as DateTime
set #dateInput = '2012-01-17'
select *
from tablName
where startDateTime >= #dateInput
and startDateTime < dateadd(day, 1, #dateInput)
Note: SQL Server 2008+ has a new data type Date with no time component that can make these types of queries more readable.
There is now more information so I'll add a more appropriate answer.
The requirements are now a stored procedure passed a Date type parameter, not DateTime, and the desire is to return rows from a table based on criterion against a DateTime field named StartDateTime...
create procedure dbo.spGetEntriesForOneDay
#DesiredDate DateTime
as
SET NOCOUNT ON;
SET #DesiredDate = DATEADD(day, DATEDIFF(day, 0, #DesiredDate), 0)
SELECT Field1, Field2 -- see note 1
FROM dbo.TableName
WHERE StartDateTime >= #DesiredDate -- see note 2
AND StartDateTime < DATEADD(day, 1, #DesiredDate) -- see note 3
NOTE 1: Don't use * in production code, especially in a stored procedure. Besides being wasteful by returning columns you probably don't need and precluding the optimization of a covering index on a subset of the columns required you would need to recompile this stored procedure whenever the underlying table is altered in order to avoid unpredictable results.
NOTE 2: Avoid wrapping fields in functions. A field not wrapped in a function can potentially be matched by the optimizer to an index while a field wrapped in a function never will.
NOTE 3: #Martin Smith and #RedFilter are correct in that .997 precision assumes DateTime datatype forever; this approach is more future proof because is makes no assumptions of data type precision.
You're using a datetime field (I'm guessing).
Don't forget the time:
select * from tablName
where startDateTime between '2012-01-17' and '2012-01-17 23:59:59.997'
You can use the DateDiff function in the where clause. It would look like this:
select col1, col2 from tablName where DateDiff(day, startDateTime, #DesiredDate) = 0

Compare DATETIME and DATE ignoring time portion

I have two tables where column [date] is type of DATETIME2(0).
I have to compare two records only by theirs Date parts (day+month+year), discarding Time parts (hours+minutes+seconds).
How can I do that?
Use the CAST to the new DATE data type in SQL Server 2008 to compare just the date portion:
IF CAST(DateField1 AS DATE) = CAST(DateField2 AS DATE)
A small drawback in Marc's answer is that both datefields have been typecast, meaning you'll be unable to leverage any indexes.
So, if there is a need to write a query that can benefit from an index on a date field, then the following (rather convoluted) approach is necessary.
The indexed datefield (call it DF1) must be untouched by any kind of function.
So you have to compare DF1 to the full range of datetime values for the day of DF2.
That is from the date-part of DF2, to the date-part of the day after DF2.
I.e. (DF1 >= CAST(DF2 AS DATE)) AND (DF1 < DATEADD(dd, 1, CAST(DF2 AS DATE)))
NOTE: It is very important that the comparison is >= (equality allowed) to the date of DF2, and (strictly) < the day after DF2. Also the BETWEEN operator doesn't work because it permits equality on both sides.
PS: Another means of extracting the date only (in older versions of SQL Server) is to use a trick of how the date is represented internally.
Cast the date as a float.
Truncate the fractional part
Cast the value back to a datetime
I.e. CAST(FLOOR(CAST(DF2 AS FLOAT)) AS DATETIME)
Though I upvoted the answer marked as correct. I wanted to touch on a few things for anyone stumbling upon this.
In general, if you're filtering specifically on Date values alone. Microsoft recommends using the language neutral format of ymd or y-m-d.
Note that the form '2007-02-12' is considered language-neutral only
for the data types DATE, DATETIME2, and DATETIMEOFFSET.
To do a date comparison using the aforementioned approach is simple. Consider the following, contrived example.
--112 is ISO format 'YYYYMMDD'
declare #filterDate char(8) = CONVERT(char(8), GETDATE(), 112)
select
*
from
Sales.Orders
where
CONVERT(char(8), OrderDate, 112) = #filterDate
In a perfect world, performing any manipulation to the filtered column should be avoided because this can prevent SQL Server from using indexes efficiently. That said, if the data you're storing is only ever concerned with the date and not time, consider storing as DATETIME with midnight as the time. Because:
When SQL Server converts the literal to the filtered column’s type, it
assumes midnight when a time part isn’t indicated. If you want such a
filter to return all rows from the specified date, you need to ensure
that you store all values with midnight as the time.
Thus, assuming you are only concerned with date, and store your data as such. The above query can be simplified to:
--112 is ISO format 'YYYYMMDD'
declare #filterDate char(8) = CONVERT(char(8), GETDATE(), 112)
select
*
from
Sales.Orders
where
OrderDate = #filterDate
You can try this one
CONVERT(DATE, GETDATE()) = CONVERT(DATE,'2017-11-16 21:57:20.000')
I test that for MS SQL 2014 by following code
select case when CONVERT(DATE, GETDATE()) = CONVERT(DATE,'2017-11-16 21:57:20.000') then 'ok'
else '' end
You may use DateDiff and compare by day.
DateDiff(dd,#date1,#date2) > 0
It means #date2 > #date1
For example :
select DateDiff(dd, '01/01/2021 10:20:00', '02/01/2021 10:20:00')
has the result : 1
For Compare two date like MM/DD/YYYY to MM/DD/YYYY .
Remember First thing column type of Field must be dateTime.
Example : columnName : payment_date dataType : DateTime .
after that you can easily compare it.
Query is :
select * from demo_date where date >= '3/1/2015' and date <= '3/31/2015'.
It very simple ......
It tested it.....

Best approach to remove time part of datetime in SQL Server

Which method provides the best performance when removing the time portion from a datetime field in SQL Server?
a) select DATEADD(dd, DATEDIFF(dd, 0, getdate()), 0)
or
b) select cast(convert(char(11), getdate(), 113) as datetime)
The second method does send a few more bytes either way but that might not be as important as the speed of the conversion.
Both also appear to be very fast, but there might be a difference in speed when dealing with hundreds-of-thousands or more rows?
Also, is it possible that there are even better methods to get rid of the time portion of a datetime in SQL?
Strictly, method a is the least resource intensive:
a) select DATEADD(dd, DATEDIFF(dd, 0, getdate()), 0)
Proven less CPU intensive for the same total duration a million rows by someone with way too much time on their hands: Most efficient way in SQL Server to get a date from date+time?
I saw a similar test elsewhere with similar results too.
I prefer the DATEADD/DATEDIFF because:
varchar is subject to language/dateformat issues
Example: Why is my CASE expression non-deterministic?
float relies on internal storage
it extends to work out first day of month, tomorrow, etc by changing "0" base
Edit, Oct 2011
For SQL Server 2008+, you can CAST to date i.e. CAST(getdate() AS date). Or just use date datatype so no time to remove.
Edit, Jan 2012
A worked example of how flexible this is: Need to calculate by rounded time or date figure in sql server
Edit, May 2012
Do not use this in WHERE clauses and the like without thinking: adding a function or CAST to a column invalidates index usage. See number 2 here Common SQL Programming Mistakes
Now, this does have an example of later SQL Server optimiser versions managing CAST to date correctly, but generally it will be a bad idea ...
Edit, Sep 2018, for datetime2
DECLARE #datetime2value datetime2 = '02180912 11:45' --this is deliberately within datetime2, year 0218
DECLARE #datetime2epoch datetime2 = '19000101'
select DATEADD(dd, DATEDIFF(dd, #datetime2epoch, #datetime2value), #datetime2epoch)
In SQL Server 2008, you can use:
CONVERT(DATE, getdate(), 101)
Of-course this is an old thread but to make it complete.
From SQL 2008 you can use DATE datatype so you can simply do:
SELECT CONVERT(DATE,GETDATE())
In SQL Server 2008, there is a DATE datetype (also a TIME datatype).
CAST(GetDate() as DATE)
or
declare #Dt as DATE = GetDate()
SELECT CAST(FLOOR(CAST(getdate() AS FLOAT)) AS DATETIME)
...is not a good solution, per the comments below.
I would delete this answer, but I'll leave it here as a counter-example since I think the commenters' explanation of why it's not a good idea is still useful.
Here's yet another answer, from another duplicate question:
SELECT CAST(CAST(getutcdate() - 0.50000004 AS int) AS datetime)
This magic number method performs slightly faster than the DATEADD method. (It looks like ~10%)
The CPU Time on several rounds of a million records:
DATEADD MAGIC FLOAT
500 453
453 360
375 375
406 360
But note that these numbers are possibly irrelevant because they are already VERY fast. Unless I had record sets of 100,000 or more, I couldn't even get the CPU Time to read above zero.
Considering the fact that DateAdd is meant for this purpose and is more robust, I'd say use DateAdd.
SELECT CAST(CAST(GETDATE() AS DATE) AS DATETIME)
I really like:
[date] = CONVERT(VARCHAR(10), GETDATE(), 120)
The 120 format code will coerce the date into the ISO 8601 standard:
'YYYY-MM-DD' or '2017-01-09'
Super easy to use in dplyr (R) and pandas (Python)!
BEWARE!
Method a) and b) does NOT always have the same output!
select DATEADD(dd, DATEDIFF(dd, 0, '2013-12-31 23:59:59.999'), 0)
Output: 2014-01-01 00:00:00.000
select cast(convert(char(11), '2013-12-31 23:59:59.999', 113) as datetime)
Output: 2013-12-31 00:00:00.000
(Tested on MS SQL Server 2005 and 2008 R2)
EDIT: According to Adam's comment, this cannot happen if you read the date value from the table, but it can happen if you provide your date value as a literal (example: as a parameter of a stored procedure called via ADO.NET).
See this question:
How can I truncate a datetime in SQL Server?
Whatever you do, don't use the string method. That's about the worst way you could do it.
Already answered but ill throw this out there too...
this suposedly also preforms well but it works by throwing away the decimal (which stores time) from the float and returning only whole part (which is date)
CAST(
FLOOR( CAST( GETDATE() AS FLOAT ) )
AS DATETIME
)
second time I found this solution... i grabbed this code off
CAST(round(cast(getdate()as real),0,1) AS datetime)
This method does not use string function. Date is basically a real datatype with digits before decimal are fraction of a day.
this I guess will be faster than a lot.
For me the code below is always a winner:
SELECT CONVERT(DATETIME, FLOOR(CONVERT(FLOAT,GETDATE())));
select CONVERT(char(10), GetDate(),126)
Strip time on inserts/updates in the first place. As for on-the-fly conversion, nothing can beat a user-defined function maintanability-wise:
select date_only(dd)
The implementation of date_only can be anything you like - now it's abstracted away and calling code is much much cleaner.
I think you mean
cast(floor(cast(getdate()as float))as datetime)
real is only 32-bits, and could lose some information
This is fastest
cast(cast(getdate()+x-0.5 as int)as datetime)
...though only about 10% faster(about 0.49 microseconds CPU vs. 0.58)
This was recommended, and takes the same time in my test just now:
DATEADD(dd, DATEDIFF(dd, 0, getdate()), 0)
In SQL 2008, the SQL CLR function is about 5 times faster than using a SQL function would be, at 1.35 microseconds versus 6.5 microsections, indicating much lower function-call overhead for a SQL CLR function versus a simple SQL UDF.
In SQL 2005, the SQL CLR function is 16 times faster, per my testing, versus this slow function:
create function dateonly ( #dt datetime )
returns datetime
as
begin
return cast(floor(cast(#dt as float))as int)
end
How about select cast(cast my_datetime_field as date) as datetime)? This results in the same date, with the time set to 00:00, but avoids any conversion to text and also avoids any explicit numeric rounding.
I think that if you stick strictly with TSQL that this is the fastest way to truncate the time:
select convert(datetime,convert(int,convert(float,[Modified])))
I found this truncation method to be about 5% faster than the DateAdd method. And this can be easily modified to round to the nearest day like this:
select convert(datetime,ROUND(convert(float,[Modified]),0))
Here I made a function to remove some parts of a datetime for SQL Server. Usage:
First param is the datetime to be stripped off.
Second param is a char:
s: rounds to seconds; removes milliseconds
m: rounds to minutes; removes seconds and milliseconds
h: rounds to hours; removes minutes, seconds and milliseconds.
d: rounds to days; removes hours, minutes, seconds and milliseconds.
Returns the new datetime
create function dbo.uf_RoundDateTime(#dt as datetime, #part as char)
returns datetime
as
begin
if CHARINDEX( #part, 'smhd',0) = 0 return #dt;
return cast(
Case #part
when 's' then convert(varchar(19), #dt, 126)
when 'm' then convert(varchar(17), #dt, 126) + '00'
when 'h' then convert(varchar(14), #dt, 126) + '00:00'
when 'd' then convert(varchar(14), #dt, 112)
end as datetime )
end
Just in case anyone is looking in here for a Sybase version since several of the versions above didn't work
CAST(CONVERT(DATE,GETDATE(),103) AS DATETIME)
Tested in I SQL v11 running on Adaptive Server 15.7
If possible, for special things like this, I like to use CLR functions.
In this case:
[Microsoft.SqlServer.Server.SqlFunction]
public static SqlDateTime DateOnly(SqlDateTime input)
{
if (!input.IsNull)
{
SqlDateTime dt = new SqlDateTime(input.Value.Year, input.Value.Month, input.Value.Day, 0, 0, 0);
return dt;
}
else
return SqlDateTime.Null;
}
I, personally, almost always use User Defined functions for this if dealing with SQL Server 2005 (or lower version), however, it should be noted that there are specific drawbacks to using UDF's, especially if applying them to WHERE clauses (see below and the comments on this answer for further details). If using SQL Server 2008 (or higher) - see below.
In fact, for most databases that I create, I add these UDF's in right near the start since I know there's a 99% chance I'm going to need them sooner or later.
I create one for "date only" & "time only" (although the "date only" one is by far the most used of the two).
Here's some links to a variety of date-related UDF's:
Essential SQL Server Date, Time and DateTime Functions
Get Date Only Function
That last link shows no less than 3 different ways to getting the date only part of a datetime field and mentions some pros and cons of each approach.
If using a UDF, it should be noted that you should try to avoid using the UDF as part of a WHERE clause in a query as this will greatly hinder performance of the query. The main reason for this is that using a UDF in a WHERE clause renders that clause as non-sargable, which means that SQL Server can no longer use an index with that clause in order to improve the speed of query execution. With reference to my own usage of UDF's, I'll frequently use the "raw" date column within the WHERE clause, but apply the UDF to the SELECTed column. In this way, the UDF is only applied to the filtered result-set and not every row of the table as part of the filter.
Of course, the absolute best approach for this is to use SQL Server 2008 (or higher) and separate out your dates and times, as the SQL Server database engine is then natively providing the individual date and time components, and can efficiently query these independently without the need for a UDF or other mechanism to extract either the date or time part from a composite datetime type.
I would use:
CAST
(
CAST(YEAR(DATEFIELD) as varchar(4)) + '/' CAST(MM(DATEFIELD) as varchar(2)) + '/' CAST(DD(DATEFIELD) as varchar(2)) as datetime
)
Thus effectively creating a new field from the date field you already have.

How to make faster this statement : "paramDate Between startDate and NULL"?

This query is taking long time when endDate is null (i think that its about case statement, before case statement it was fast)
SELECT *
FROM HastaKurumlari
WHERE CONVERT(SMALLDATETIME,'21-05-2009',103)
BETWEEN startDate
AND (CASE WHEN endDate IS NULL THEN GETDATE() ELSE endDate END)
What should i use, when endDate is null to make it faster ?
Here's the query without CONVERT or CASE:
SELECT *
FROM HastaKurumlari
WHERE '21-05-2009' between startDate and IsNull(endDate,getdate())
To make sure Sql Server doens't evaluate getdate() for every row, you could cache it, although I'm pretty sure Sql Server is smart enough by default:
declare #now datetime
set #now = getdate()
SELECT *
FROM HastaKurumlari
WHERE '21-05-2009' between startDate and IsNull(endDate,#now)
Posting the query plan could help explain why the query is slow:
SET SHOWPLAN_TEXT ON
go
SELECT *
FROM HastaKurumlari
WHERE CONVERT(SMALLDATETIME,'21-05-2009',103)
BETWEEN startDate
AND (CASE WHEN endDate IS NULL THEN GETDATE() ELSE endDate END)
If it is performance critical, then perhaps just don't use null for the open end-date - use the maximum supported datetime instead (probably lots of 9s).
I'd also do the conversion separately:
DECLARE #when datetime
SET #when = CONVERT(SMALLDATETIME,'21-05-2009',103)
SELECT *
FROM HastaKurumlari
WHERE #when
BETWEEN startDate AND endDate
There is still something a bit different in the above and your original; if you can explain the intent of the GETDATE() check I might be able to tidy (read:fix) it a bit.
As a starting point, factor out GETDATE() so that its called just once, and you should see an improvement in speed.
The way you've written it you are asking for GETDATE() to be evaluated every time enddate is null.
Since GETDATE() is a non-deterministic function the query cannot be optimised and will tend to under perform.
You could try the coalesce function:
select *
from HastaKurumlari
where convert(smalldatetime, '21-05-2009', 103)
between startDate and coalesce(endDate, getdate());
The only way to be certain is to try any alternatives and view the execution plan generated for each query.