Using indexes when comparing datetimesHow does database indexing work?How to return only the Date from a SQL Server DateTime datatypeWhen should static_cast, dynamic_cast, const_cast and reinterpret_cast be used?Should I use the datetime or timestamp data type in MySQL?Converting string into datetimeCompare two dates with JavaScriptWhat do Clustered and Non clustered index actually mean?DateTime2 vs DateTime in SQL Serverindexing datetime column on a very wide tableOptimal Index on datetime colum for queries on day
In the time of the mishna, were there Jewish cities without courts?
Pulling the rope with one hand is as heavy as with two hands?
How to replace the "space symbol" (squat-u) in listings?
How to set printing options as reverse order as default on 18.04
Multiple options for Pseudonyms
How to stop co-workers from teasing me because I know Russian?
How to back up a running remote server?
Do I have an "anti-research" personality?
Please, smoke with good manners
Packing rectangles: Does rotation ever help?
Phrase for the opposite of "foolproof"
Does jamais mean always or never in this context?
Why do computer-science majors learn calculus?
You look catfish vs You look like a catfish
Did Henry V’s archers at Agincourt fight with no pants / breeches on because of dysentery?
Can a creature tell when it has been affected by a Divination wizard's Portent?
How to set the font color of quantity objects (Version 11.3 vs version 12)?
Asahi Dry Black beer can
What's the metal clinking sound at the end of credits in Avengers: Endgame?
What does 「再々起」mean?
Confused by notation of atomic number Z and mass number A on periodic table of elements
Minimum value of 4 digit number divided by sum of its digits
Why does processed meat contain preservatives, while canned fish needs not?
Is it possible to Ready a spell to be cast just before the start of your next turn by having the trigger be an ally's attack?
Using indexes when comparing datetimes
How does database indexing work?How to return only the Date from a SQL Server DateTime datatypeWhen should static_cast, dynamic_cast, const_cast and reinterpret_cast be used?Should I use the datetime or timestamp data type in MySQL?Converting string into datetimeCompare two dates with JavaScriptWhat do Clustered and Non clustered index actually mean?DateTime2 vs DateTime in SQL Serverindexing datetime column on a very wide tableOptimal Index on datetime colum for queries on day
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty height:90px;width:728px;box-sizing:border-box;
I have two tables, both of which containing millions of rows of data.
tbl_one:
purchasedtm DATETIME,
userid INT,
totalcost INT
tbl_two:
id BIGINT,
eventdtm DATETIME,
anothercol INT
The first table has a clustered index on the first two columns: CLUSTERED INDEX tbl_one_idx ON(purchasedtm, userid)
The second one has a primary key on its ID column, and also a non-clustered index on the eventdtm
column.
I want to run a query which looks for rows in which purchasedtm
and eventdtm
are on the same day.
Originally, I wrote my query as:
WHERE CAST(tbl_one.purchasedtm AS DATE) = CAST(tbl_two.eventdtm AS DATE)
But this was not going to use either of the two indexes.
Later, I changed my query to this:
WHERE tbl_one.purchasedtm >= CAST(tbl_two.eventdtm AS DATE)
AND tbl_one.purchasedtm < DATEADD(DAY, 1, CAST(tbl_two.eventdtm AS DATE))
This way, because only one side of the comparison is wrapped in a function, the other side can still use its index. Correct?
I also have some additional questions:
- I can write the query the other way around too, i.e. keeping
tbl_two.eventdtm
untouched and wrappingtbl_one.purchasedtm
inCAST()
. Would that make a difference in performance? - If the answer to the previous question is yes is it because
eventdtm
has its own dedicated index, while looking uppurcahsedtm
would only be a partial index match? - Are there other factors I can take into consideration for deciding which of the two choices is better? (For example, if there are millions of rows in
tbl_one
but billions of rows intbl_two
, would that impact which column I should CAST and which one I should not?) - In genera, if you compare two columns that are both indexed, would we gain any performance compared to a similar scenario in which only one of them is indexed?
- And lastly, can I perform my original task without using CAST?
Note: I do not have the ability to create or modify indexes, add columns, etc.
sql-server datetime indexing casting sql-server-2016
|
show 2 more comments
I have two tables, both of which containing millions of rows of data.
tbl_one:
purchasedtm DATETIME,
userid INT,
totalcost INT
tbl_two:
id BIGINT,
eventdtm DATETIME,
anothercol INT
The first table has a clustered index on the first two columns: CLUSTERED INDEX tbl_one_idx ON(purchasedtm, userid)
The second one has a primary key on its ID column, and also a non-clustered index on the eventdtm
column.
I want to run a query which looks for rows in which purchasedtm
and eventdtm
are on the same day.
Originally, I wrote my query as:
WHERE CAST(tbl_one.purchasedtm AS DATE) = CAST(tbl_two.eventdtm AS DATE)
But this was not going to use either of the two indexes.
Later, I changed my query to this:
WHERE tbl_one.purchasedtm >= CAST(tbl_two.eventdtm AS DATE)
AND tbl_one.purchasedtm < DATEADD(DAY, 1, CAST(tbl_two.eventdtm AS DATE))
This way, because only one side of the comparison is wrapped in a function, the other side can still use its index. Correct?
I also have some additional questions:
- I can write the query the other way around too, i.e. keeping
tbl_two.eventdtm
untouched and wrappingtbl_one.purchasedtm
inCAST()
. Would that make a difference in performance? - If the answer to the previous question is yes is it because
eventdtm
has its own dedicated index, while looking uppurcahsedtm
would only be a partial index match? - Are there other factors I can take into consideration for deciding which of the two choices is better? (For example, if there are millions of rows in
tbl_one
but billions of rows intbl_two
, would that impact which column I should CAST and which one I should not?) - In genera, if you compare two columns that are both indexed, would we gain any performance compared to a similar scenario in which only one of them is indexed?
- And lastly, can I perform my original task without using CAST?
Note: I do not have the ability to create or modify indexes, add columns, etc.
sql-server datetime indexing casting sql-server-2016
3
WHERE CAST(tbl_one.purchasedtm AS DATE) = CAST(tbl_two.eventdtm AS DATE)
"But this was not going to use either of the two indexes." Wrong.CAST([column] AS date)
is SARGable/ SARGable functions in SQL Server
– Larnu
Mar 15 at 18:53
1
Ideally the userid would be a PK for table 1 and you'd have another table with all of the purchases. Are you joining these on the IT fields? If so, swapping the order of that clustered key would help.
– scsimon
Mar 15 at 18:53
Is your revised query using the index?
– Salman A
Mar 15 at 19:29
@Larnu I did not know that! Very interesting. I will read more and update my question. But can you respond to it in a non-SARGable context? (e.g. if I was using a CAST that was not SRGable, or a function other than CAST)
– Merik
Mar 18 at 2:18
Most of the time applying any function to a Column in yourWHERE
will make it non-SARGable. The only one I can think of is actually SARGable isCAST(column,AS date)
. I don't remember for sure off the top of my head, but I thinkCONVERT(int,DecimalColumn)
is SARGable. The most common thing you (I) see are things likeWHERE ISNULL(MyColumn,0) = ISNULL(@MyVariable,0)
which isn't SARGable. Something like that would be better written using Boolean logicWHERE (MyColumn = @MyVariable OR (MyColumn IS NULL AND @MyVariable IS NULL))
.
– Larnu
Mar 18 at 8:54
|
show 2 more comments
I have two tables, both of which containing millions of rows of data.
tbl_one:
purchasedtm DATETIME,
userid INT,
totalcost INT
tbl_two:
id BIGINT,
eventdtm DATETIME,
anothercol INT
The first table has a clustered index on the first two columns: CLUSTERED INDEX tbl_one_idx ON(purchasedtm, userid)
The second one has a primary key on its ID column, and also a non-clustered index on the eventdtm
column.
I want to run a query which looks for rows in which purchasedtm
and eventdtm
are on the same day.
Originally, I wrote my query as:
WHERE CAST(tbl_one.purchasedtm AS DATE) = CAST(tbl_two.eventdtm AS DATE)
But this was not going to use either of the two indexes.
Later, I changed my query to this:
WHERE tbl_one.purchasedtm >= CAST(tbl_two.eventdtm AS DATE)
AND tbl_one.purchasedtm < DATEADD(DAY, 1, CAST(tbl_two.eventdtm AS DATE))
This way, because only one side of the comparison is wrapped in a function, the other side can still use its index. Correct?
I also have some additional questions:
- I can write the query the other way around too, i.e. keeping
tbl_two.eventdtm
untouched and wrappingtbl_one.purchasedtm
inCAST()
. Would that make a difference in performance? - If the answer to the previous question is yes is it because
eventdtm
has its own dedicated index, while looking uppurcahsedtm
would only be a partial index match? - Are there other factors I can take into consideration for deciding which of the two choices is better? (For example, if there are millions of rows in
tbl_one
but billions of rows intbl_two
, would that impact which column I should CAST and which one I should not?) - In genera, if you compare two columns that are both indexed, would we gain any performance compared to a similar scenario in which only one of them is indexed?
- And lastly, can I perform my original task without using CAST?
Note: I do not have the ability to create or modify indexes, add columns, etc.
sql-server datetime indexing casting sql-server-2016
I have two tables, both of which containing millions of rows of data.
tbl_one:
purchasedtm DATETIME,
userid INT,
totalcost INT
tbl_two:
id BIGINT,
eventdtm DATETIME,
anothercol INT
The first table has a clustered index on the first two columns: CLUSTERED INDEX tbl_one_idx ON(purchasedtm, userid)
The second one has a primary key on its ID column, and also a non-clustered index on the eventdtm
column.
I want to run a query which looks for rows in which purchasedtm
and eventdtm
are on the same day.
Originally, I wrote my query as:
WHERE CAST(tbl_one.purchasedtm AS DATE) = CAST(tbl_two.eventdtm AS DATE)
But this was not going to use either of the two indexes.
Later, I changed my query to this:
WHERE tbl_one.purchasedtm >= CAST(tbl_two.eventdtm AS DATE)
AND tbl_one.purchasedtm < DATEADD(DAY, 1, CAST(tbl_two.eventdtm AS DATE))
This way, because only one side of the comparison is wrapped in a function, the other side can still use its index. Correct?
I also have some additional questions:
- I can write the query the other way around too, i.e. keeping
tbl_two.eventdtm
untouched and wrappingtbl_one.purchasedtm
inCAST()
. Would that make a difference in performance? - If the answer to the previous question is yes is it because
eventdtm
has its own dedicated index, while looking uppurcahsedtm
would only be a partial index match? - Are there other factors I can take into consideration for deciding which of the two choices is better? (For example, if there are millions of rows in
tbl_one
but billions of rows intbl_two
, would that impact which column I should CAST and which one I should not?) - In genera, if you compare two columns that are both indexed, would we gain any performance compared to a similar scenario in which only one of them is indexed?
- And lastly, can I perform my original task without using CAST?
Note: I do not have the ability to create or modify indexes, add columns, etc.
sql-server datetime indexing casting sql-server-2016
sql-server datetime indexing casting sql-server-2016
edited Mar 18 at 2:14
Merik
asked Mar 15 at 18:50
MerikMerik
1,23431327
1,23431327
3
WHERE CAST(tbl_one.purchasedtm AS DATE) = CAST(tbl_two.eventdtm AS DATE)
"But this was not going to use either of the two indexes." Wrong.CAST([column] AS date)
is SARGable/ SARGable functions in SQL Server
– Larnu
Mar 15 at 18:53
1
Ideally the userid would be a PK for table 1 and you'd have another table with all of the purchases. Are you joining these on the IT fields? If so, swapping the order of that clustered key would help.
– scsimon
Mar 15 at 18:53
Is your revised query using the index?
– Salman A
Mar 15 at 19:29
@Larnu I did not know that! Very interesting. I will read more and update my question. But can you respond to it in a non-SARGable context? (e.g. if I was using a CAST that was not SRGable, or a function other than CAST)
– Merik
Mar 18 at 2:18
Most of the time applying any function to a Column in yourWHERE
will make it non-SARGable. The only one I can think of is actually SARGable isCAST(column,AS date)
. I don't remember for sure off the top of my head, but I thinkCONVERT(int,DecimalColumn)
is SARGable. The most common thing you (I) see are things likeWHERE ISNULL(MyColumn,0) = ISNULL(@MyVariable,0)
which isn't SARGable. Something like that would be better written using Boolean logicWHERE (MyColumn = @MyVariable OR (MyColumn IS NULL AND @MyVariable IS NULL))
.
– Larnu
Mar 18 at 8:54
|
show 2 more comments
3
WHERE CAST(tbl_one.purchasedtm AS DATE) = CAST(tbl_two.eventdtm AS DATE)
"But this was not going to use either of the two indexes." Wrong.CAST([column] AS date)
is SARGable/ SARGable functions in SQL Server
– Larnu
Mar 15 at 18:53
1
Ideally the userid would be a PK for table 1 and you'd have another table with all of the purchases. Are you joining these on the IT fields? If so, swapping the order of that clustered key would help.
– scsimon
Mar 15 at 18:53
Is your revised query using the index?
– Salman A
Mar 15 at 19:29
@Larnu I did not know that! Very interesting. I will read more and update my question. But can you respond to it in a non-SARGable context? (e.g. if I was using a CAST that was not SRGable, or a function other than CAST)
– Merik
Mar 18 at 2:18
Most of the time applying any function to a Column in yourWHERE
will make it non-SARGable. The only one I can think of is actually SARGable isCAST(column,AS date)
. I don't remember for sure off the top of my head, but I thinkCONVERT(int,DecimalColumn)
is SARGable. The most common thing you (I) see are things likeWHERE ISNULL(MyColumn,0) = ISNULL(@MyVariable,0)
which isn't SARGable. Something like that would be better written using Boolean logicWHERE (MyColumn = @MyVariable OR (MyColumn IS NULL AND @MyVariable IS NULL))
.
– Larnu
Mar 18 at 8:54
3
3
WHERE CAST(tbl_one.purchasedtm AS DATE) = CAST(tbl_two.eventdtm AS DATE)
"But this was not going to use either of the two indexes." Wrong. CAST([column] AS date)
is SARGable/ SARGable functions in SQL Server– Larnu
Mar 15 at 18:53
WHERE CAST(tbl_one.purchasedtm AS DATE) = CAST(tbl_two.eventdtm AS DATE)
"But this was not going to use either of the two indexes." Wrong. CAST([column] AS date)
is SARGable/ SARGable functions in SQL Server– Larnu
Mar 15 at 18:53
1
1
Ideally the userid would be a PK for table 1 and you'd have another table with all of the purchases. Are you joining these on the IT fields? If so, swapping the order of that clustered key would help.
– scsimon
Mar 15 at 18:53
Ideally the userid would be a PK for table 1 and you'd have another table with all of the purchases. Are you joining these on the IT fields? If so, swapping the order of that clustered key would help.
– scsimon
Mar 15 at 18:53
Is your revised query using the index?
– Salman A
Mar 15 at 19:29
Is your revised query using the index?
– Salman A
Mar 15 at 19:29
@Larnu I did not know that! Very interesting. I will read more and update my question. But can you respond to it in a non-SARGable context? (e.g. if I was using a CAST that was not SRGable, or a function other than CAST)
– Merik
Mar 18 at 2:18
@Larnu I did not know that! Very interesting. I will read more and update my question. But can you respond to it in a non-SARGable context? (e.g. if I was using a CAST that was not SRGable, or a function other than CAST)
– Merik
Mar 18 at 2:18
Most of the time applying any function to a Column in your
WHERE
will make it non-SARGable. The only one I can think of is actually SARGable isCAST(column,AS date)
. I don't remember for sure off the top of my head, but I think CONVERT(int,DecimalColumn)
is SARGable. The most common thing you (I) see are things like WHERE ISNULL(MyColumn,0) = ISNULL(@MyVariable,0)
which isn't SARGable. Something like that would be better written using Boolean logic WHERE (MyColumn = @MyVariable OR (MyColumn IS NULL AND @MyVariable IS NULL))
.– Larnu
Mar 18 at 8:54
Most of the time applying any function to a Column in your
WHERE
will make it non-SARGable. The only one I can think of is actually SARGable isCAST(column,AS date)
. I don't remember for sure off the top of my head, but I think CONVERT(int,DecimalColumn)
is SARGable. The most common thing you (I) see are things like WHERE ISNULL(MyColumn,0) = ISNULL(@MyVariable,0)
which isn't SARGable. Something like that would be better written using Boolean logic WHERE (MyColumn = @MyVariable OR (MyColumn IS NULL AND @MyVariable IS NULL))
.– Larnu
Mar 18 at 8:54
|
show 2 more comments
2 Answers
2
active
oldest
votes
Little. late after commenting but...
As discussed in the comments, code such as CAST(DateTimeColumn AS date)
is actually SARGable. Rob Farley posted an article on some of the SARGable and non-SARGable functionality here, however, I'll cover a few things off anyway.
Firstly, applying a function to a column will normally make your query non-SARGable, and especially if it changes the order of the values or the order of them is meaningless. Take something like:
SELECT *
FROM TABLE
WHERE RIGHT(COLUMN,5) = 'value';
The order of the values in the column are utterly unhelpful here, as we're focusing on the right hand characters. Unfortunately, as Rob also discusses:
SELECT *
FROM TABLE
WHERE LEFT(COLUMN,5) = 'value';
This is also non-SARGable. However what about the following?
SELECT *
FROM TABLE
WHERE Column LIKE 'value%';
This is, as the logic isn't applied to the column and the order doesn't change. If the value wehre '%value%'
then that too would be non-SARGable.
When applying logic that adds (or subtracts) what you want to find, you always want to apply that to the literal value (or function, like GETDATE()`). For example one of these expressions is SARGable the other is not:
Column + 1 = @Variable --non-SARGable
Column = @Variable - 1 --SARGable
The same applies to things like DATEADD
@DateVariable BETWEEN DateColumn AND DATEADD(DAY, 30,DateColumn) --non-SARGable
DateColumn BETWEEN DATEADD(DAY, -30, @DateVariable) AND @DateVariable --SARGable
Changing the datatype (other than to a date
) rarely will keep a query SARGable. CONVERT(date,varchardate,112)
will not be SARGable, even though the order of the column is unchanged. Converting an decimal
to an int
, however, had the same result as converting a datetime
to a date
, and kept SARGability:
CREATE TABLE testtab (n decimal(2,1) PRIMARY KEY CLUSTERED);
INSERT INTO testtab
VALUES(0.1),
(0.3),
(1.1),
(1.7),
(2.4);
GO
SELECT n
FROM testtab
WHERE CONVERT(int,n) = 2;
GO
DROP TABLE testtab;
Hopefully, that gives you enough to go on, but pelase do ask if you want me to add anything further.
add a comment |
It is possible to create a persisted computed column in both tables that contains the date part only:
purchasedt AS CAST(purchasedtm AS DATE)
eventdt AS CAST(eventdtm AS DATE)
And create an index on it.
Regarding your original query: SQL Server can translate this:
WHERE CAST(tbl_one.purchasedtm AS DATE) = CAST(tbl_two.eventdtm AS DATE)
Into something similar to:
WHERE tbl_one.purchasedtm BETWEEN -- first ms of tbl_two.eventdtm
AND -- last ms of tbl_two.eventdtm
But in your case (i) it will be have to calculate this for millions of rows inside tbl_two (ii) it will have to perform range scan inside a loop. SQL Server might not use the index.
The indexed date column will result in an equality comparison and no conversion.
add a comment |
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55189030%2fusing-indexes-when-comparing-datetimes%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
Little. late after commenting but...
As discussed in the comments, code such as CAST(DateTimeColumn AS date)
is actually SARGable. Rob Farley posted an article on some of the SARGable and non-SARGable functionality here, however, I'll cover a few things off anyway.
Firstly, applying a function to a column will normally make your query non-SARGable, and especially if it changes the order of the values or the order of them is meaningless. Take something like:
SELECT *
FROM TABLE
WHERE RIGHT(COLUMN,5) = 'value';
The order of the values in the column are utterly unhelpful here, as we're focusing on the right hand characters. Unfortunately, as Rob also discusses:
SELECT *
FROM TABLE
WHERE LEFT(COLUMN,5) = 'value';
This is also non-SARGable. However what about the following?
SELECT *
FROM TABLE
WHERE Column LIKE 'value%';
This is, as the logic isn't applied to the column and the order doesn't change. If the value wehre '%value%'
then that too would be non-SARGable.
When applying logic that adds (or subtracts) what you want to find, you always want to apply that to the literal value (or function, like GETDATE()`). For example one of these expressions is SARGable the other is not:
Column + 1 = @Variable --non-SARGable
Column = @Variable - 1 --SARGable
The same applies to things like DATEADD
@DateVariable BETWEEN DateColumn AND DATEADD(DAY, 30,DateColumn) --non-SARGable
DateColumn BETWEEN DATEADD(DAY, -30, @DateVariable) AND @DateVariable --SARGable
Changing the datatype (other than to a date
) rarely will keep a query SARGable. CONVERT(date,varchardate,112)
will not be SARGable, even though the order of the column is unchanged. Converting an decimal
to an int
, however, had the same result as converting a datetime
to a date
, and kept SARGability:
CREATE TABLE testtab (n decimal(2,1) PRIMARY KEY CLUSTERED);
INSERT INTO testtab
VALUES(0.1),
(0.3),
(1.1),
(1.7),
(2.4);
GO
SELECT n
FROM testtab
WHERE CONVERT(int,n) = 2;
GO
DROP TABLE testtab;
Hopefully, that gives you enough to go on, but pelase do ask if you want me to add anything further.
add a comment |
Little. late after commenting but...
As discussed in the comments, code such as CAST(DateTimeColumn AS date)
is actually SARGable. Rob Farley posted an article on some of the SARGable and non-SARGable functionality here, however, I'll cover a few things off anyway.
Firstly, applying a function to a column will normally make your query non-SARGable, and especially if it changes the order of the values or the order of them is meaningless. Take something like:
SELECT *
FROM TABLE
WHERE RIGHT(COLUMN,5) = 'value';
The order of the values in the column are utterly unhelpful here, as we're focusing on the right hand characters. Unfortunately, as Rob also discusses:
SELECT *
FROM TABLE
WHERE LEFT(COLUMN,5) = 'value';
This is also non-SARGable. However what about the following?
SELECT *
FROM TABLE
WHERE Column LIKE 'value%';
This is, as the logic isn't applied to the column and the order doesn't change. If the value wehre '%value%'
then that too would be non-SARGable.
When applying logic that adds (or subtracts) what you want to find, you always want to apply that to the literal value (or function, like GETDATE()`). For example one of these expressions is SARGable the other is not:
Column + 1 = @Variable --non-SARGable
Column = @Variable - 1 --SARGable
The same applies to things like DATEADD
@DateVariable BETWEEN DateColumn AND DATEADD(DAY, 30,DateColumn) --non-SARGable
DateColumn BETWEEN DATEADD(DAY, -30, @DateVariable) AND @DateVariable --SARGable
Changing the datatype (other than to a date
) rarely will keep a query SARGable. CONVERT(date,varchardate,112)
will not be SARGable, even though the order of the column is unchanged. Converting an decimal
to an int
, however, had the same result as converting a datetime
to a date
, and kept SARGability:
CREATE TABLE testtab (n decimal(2,1) PRIMARY KEY CLUSTERED);
INSERT INTO testtab
VALUES(0.1),
(0.3),
(1.1),
(1.7),
(2.4);
GO
SELECT n
FROM testtab
WHERE CONVERT(int,n) = 2;
GO
DROP TABLE testtab;
Hopefully, that gives you enough to go on, but pelase do ask if you want me to add anything further.
add a comment |
Little. late after commenting but...
As discussed in the comments, code such as CAST(DateTimeColumn AS date)
is actually SARGable. Rob Farley posted an article on some of the SARGable and non-SARGable functionality here, however, I'll cover a few things off anyway.
Firstly, applying a function to a column will normally make your query non-SARGable, and especially if it changes the order of the values or the order of them is meaningless. Take something like:
SELECT *
FROM TABLE
WHERE RIGHT(COLUMN,5) = 'value';
The order of the values in the column are utterly unhelpful here, as we're focusing on the right hand characters. Unfortunately, as Rob also discusses:
SELECT *
FROM TABLE
WHERE LEFT(COLUMN,5) = 'value';
This is also non-SARGable. However what about the following?
SELECT *
FROM TABLE
WHERE Column LIKE 'value%';
This is, as the logic isn't applied to the column and the order doesn't change. If the value wehre '%value%'
then that too would be non-SARGable.
When applying logic that adds (or subtracts) what you want to find, you always want to apply that to the literal value (or function, like GETDATE()`). For example one of these expressions is SARGable the other is not:
Column + 1 = @Variable --non-SARGable
Column = @Variable - 1 --SARGable
The same applies to things like DATEADD
@DateVariable BETWEEN DateColumn AND DATEADD(DAY, 30,DateColumn) --non-SARGable
DateColumn BETWEEN DATEADD(DAY, -30, @DateVariable) AND @DateVariable --SARGable
Changing the datatype (other than to a date
) rarely will keep a query SARGable. CONVERT(date,varchardate,112)
will not be SARGable, even though the order of the column is unchanged. Converting an decimal
to an int
, however, had the same result as converting a datetime
to a date
, and kept SARGability:
CREATE TABLE testtab (n decimal(2,1) PRIMARY KEY CLUSTERED);
INSERT INTO testtab
VALUES(0.1),
(0.3),
(1.1),
(1.7),
(2.4);
GO
SELECT n
FROM testtab
WHERE CONVERT(int,n) = 2;
GO
DROP TABLE testtab;
Hopefully, that gives you enough to go on, but pelase do ask if you want me to add anything further.
Little. late after commenting but...
As discussed in the comments, code such as CAST(DateTimeColumn AS date)
is actually SARGable. Rob Farley posted an article on some of the SARGable and non-SARGable functionality here, however, I'll cover a few things off anyway.
Firstly, applying a function to a column will normally make your query non-SARGable, and especially if it changes the order of the values or the order of them is meaningless. Take something like:
SELECT *
FROM TABLE
WHERE RIGHT(COLUMN,5) = 'value';
The order of the values in the column are utterly unhelpful here, as we're focusing on the right hand characters. Unfortunately, as Rob also discusses:
SELECT *
FROM TABLE
WHERE LEFT(COLUMN,5) = 'value';
This is also non-SARGable. However what about the following?
SELECT *
FROM TABLE
WHERE Column LIKE 'value%';
This is, as the logic isn't applied to the column and the order doesn't change. If the value wehre '%value%'
then that too would be non-SARGable.
When applying logic that adds (or subtracts) what you want to find, you always want to apply that to the literal value (or function, like GETDATE()`). For example one of these expressions is SARGable the other is not:
Column + 1 = @Variable --non-SARGable
Column = @Variable - 1 --SARGable
The same applies to things like DATEADD
@DateVariable BETWEEN DateColumn AND DATEADD(DAY, 30,DateColumn) --non-SARGable
DateColumn BETWEEN DATEADD(DAY, -30, @DateVariable) AND @DateVariable --SARGable
Changing the datatype (other than to a date
) rarely will keep a query SARGable. CONVERT(date,varchardate,112)
will not be SARGable, even though the order of the column is unchanged. Converting an decimal
to an int
, however, had the same result as converting a datetime
to a date
, and kept SARGability:
CREATE TABLE testtab (n decimal(2,1) PRIMARY KEY CLUSTERED);
INSERT INTO testtab
VALUES(0.1),
(0.3),
(1.1),
(1.7),
(2.4);
GO
SELECT n
FROM testtab
WHERE CONVERT(int,n) = 2;
GO
DROP TABLE testtab;
Hopefully, that gives you enough to go on, but pelase do ask if you want me to add anything further.
answered Mar 22 at 19:14
LarnuLarnu
23.6k51933
23.6k51933
add a comment |
add a comment |
It is possible to create a persisted computed column in both tables that contains the date part only:
purchasedt AS CAST(purchasedtm AS DATE)
eventdt AS CAST(eventdtm AS DATE)
And create an index on it.
Regarding your original query: SQL Server can translate this:
WHERE CAST(tbl_one.purchasedtm AS DATE) = CAST(tbl_two.eventdtm AS DATE)
Into something similar to:
WHERE tbl_one.purchasedtm BETWEEN -- first ms of tbl_two.eventdtm
AND -- last ms of tbl_two.eventdtm
But in your case (i) it will be have to calculate this for millions of rows inside tbl_two (ii) it will have to perform range scan inside a loop. SQL Server might not use the index.
The indexed date column will result in an equality comparison and no conversion.
add a comment |
It is possible to create a persisted computed column in both tables that contains the date part only:
purchasedt AS CAST(purchasedtm AS DATE)
eventdt AS CAST(eventdtm AS DATE)
And create an index on it.
Regarding your original query: SQL Server can translate this:
WHERE CAST(tbl_one.purchasedtm AS DATE) = CAST(tbl_two.eventdtm AS DATE)
Into something similar to:
WHERE tbl_one.purchasedtm BETWEEN -- first ms of tbl_two.eventdtm
AND -- last ms of tbl_two.eventdtm
But in your case (i) it will be have to calculate this for millions of rows inside tbl_two (ii) it will have to perform range scan inside a loop. SQL Server might not use the index.
The indexed date column will result in an equality comparison and no conversion.
add a comment |
It is possible to create a persisted computed column in both tables that contains the date part only:
purchasedt AS CAST(purchasedtm AS DATE)
eventdt AS CAST(eventdtm AS DATE)
And create an index on it.
Regarding your original query: SQL Server can translate this:
WHERE CAST(tbl_one.purchasedtm AS DATE) = CAST(tbl_two.eventdtm AS DATE)
Into something similar to:
WHERE tbl_one.purchasedtm BETWEEN -- first ms of tbl_two.eventdtm
AND -- last ms of tbl_two.eventdtm
But in your case (i) it will be have to calculate this for millions of rows inside tbl_two (ii) it will have to perform range scan inside a loop. SQL Server might not use the index.
The indexed date column will result in an equality comparison and no conversion.
It is possible to create a persisted computed column in both tables that contains the date part only:
purchasedt AS CAST(purchasedtm AS DATE)
eventdt AS CAST(eventdtm AS DATE)
And create an index on it.
Regarding your original query: SQL Server can translate this:
WHERE CAST(tbl_one.purchasedtm AS DATE) = CAST(tbl_two.eventdtm AS DATE)
Into something similar to:
WHERE tbl_one.purchasedtm BETWEEN -- first ms of tbl_two.eventdtm
AND -- last ms of tbl_two.eventdtm
But in your case (i) it will be have to calculate this for millions of rows inside tbl_two (ii) it will have to perform range scan inside a loop. SQL Server might not use the index.
The indexed date column will result in an equality comparison and no conversion.
edited Mar 15 at 21:14
answered Mar 15 at 19:45
Salman ASalman A
187k68346444
187k68346444
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55189030%2fusing-indexes-when-comparing-datetimes%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
3
WHERE CAST(tbl_one.purchasedtm AS DATE) = CAST(tbl_two.eventdtm AS DATE)
"But this was not going to use either of the two indexes." Wrong.CAST([column] AS date)
is SARGable/ SARGable functions in SQL Server– Larnu
Mar 15 at 18:53
1
Ideally the userid would be a PK for table 1 and you'd have another table with all of the purchases. Are you joining these on the IT fields? If so, swapping the order of that clustered key would help.
– scsimon
Mar 15 at 18:53
Is your revised query using the index?
– Salman A
Mar 15 at 19:29
@Larnu I did not know that! Very interesting. I will read more and update my question. But can you respond to it in a non-SARGable context? (e.g. if I was using a CAST that was not SRGable, or a function other than CAST)
– Merik
Mar 18 at 2:18
Most of the time applying any function to a Column in your
WHERE
will make it non-SARGable. The only one I can think of is actually SARGable isCAST(column,AS date)
. I don't remember for sure off the top of my head, but I thinkCONVERT(int,DecimalColumn)
is SARGable. The most common thing you (I) see are things likeWHERE ISNULL(MyColumn,0) = ISNULL(@MyVariable,0)
which isn't SARGable. Something like that would be better written using Boolean logicWHERE (MyColumn = @MyVariable OR (MyColumn IS NULL AND @MyVariable IS NULL))
.– Larnu
Mar 18 at 8:54