在WHERE子句上的CAST(DATETIME AS DATE)

时间:2014-07-09 16:50:10

标签: sql-server performance tsql casting where

我正在使用SQL Server 2012,我想知道我是否写了这句话:

SELECT MyDateTimeColumn 
FROM MyTable
WHERE CAST(MyDateTimeColumn AS DATE) = '2014-07-09'

是一种较慢的方式来缩短DATETIME列的时间,我已经搜索了但是我找不到关于这个严格句子的任何内容,我不知道如何显示关于耗时转换/转换来探测它的令人印象深刻的统计数据自己。

3 个答案:

答案 0 :(得分:12)

在SQL 2008+中,CAST(foo AS date)是可以攻击的,还有一些其他的操作。查看sqlfiddle中的执行计划。

<强> SQL Fiddle Demo

答案 1 :(得分:3)

所以,让我们使用Anon的例子。我将其更改为在行号上包含主键,在日期上同时包含非群集索引。

另外,我选择创建随机日期而不是简单的增量更新。

以下是创建测试数据库的代码。

-- Do not save in physical database
USE [tempdb]
GO

-- Drop table
IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[test2]') AND type in (N'U'))
DROP TABLE test2
GO

-- Create table
CREATE TABLE test2 (my_num int NOT NULL, my_dt datetime NOT NULL);
GO

-- Add data
INSERT test2
SELECT 
    TOP 100000 
    ROW_NUMBER() OVER(ORDER BY (SELECT 1)) as my_num,
    DATEADD(minute, RAND() * 500 * ROW_NUMBER() OVER(ORDER BY (SELECT 1)), '2000-01-01') as my_dt
FROM 
    master.dbo.spt_values t1, master.dbo.spt_values t2
GO

-- Add primary key
ALTER TABLE Test2 ADD CONSTRAINT pk_My_Num PRIMARY KEY (my_num);
GO

-- Add nc index
CREATE INDEX ix_My_Dt ON Test2 (my_dt);
GO

现在,让我们来看看每个解决方案。优点和缺点。

我将使用跟踪标志来查看代数查询分析树以及查询计划。

解决方案1:索引扫描很糟糕,需要将转化应用于每个日期字段。

-- Show output to message screen
DBCC TRACEON(3604)

-- 1 - Not sargable, applies conversion to each date field
SELECT count(*)
FROM Test2 
WHERE CONVERT(varchar(10), my_dt, 120) >= '2000-02-01' 
  AND CONVERT(varchar(10), my_dt, 120) < '2000-02-02'
OPTION (RECOMPILE, QUERYTRACEON 8607)

enter image description here

enter image description here

解决方案2:索引搜索是好的,没有日期字段转换。但是,文化特定日期格式。

-- 2 - Sargable
SELECT count(*)
FROM Test2 
WHERE my_dt >= '2000-02-01' AND my_dt < '2000-02-02'
OPTION (RECOMPILE, QUERYTRACEON 8607)

enter image description here

enter image description here

解决方案3:索引搜索哪个好,日期字段没有转换。但是,您必须将日期转换为整数。

-- 3 - Implicit conversion, still Sargable
SELECT COUNT(*) FROM Test2 
WHERE my_dt >= 36555 AND my_dt < 36556
OPTION (RECOMPILE, QUERYTRACEON 8607)

enter image description here

enter image description here

解决方案4:索引搜索哪个好,日期字段没有转换。日期采用文化(国家)中立格式。最佳解决方案!

-- 4 - Sargable
SELECT count(*)
FROM Test2 
WHERE my_dt >= '20000201' AND my_dt < '20000202'
OPTION (RECOMPILE, QUERYTRACEON 8607);

enter image description here

enter image description here

解决方案5:索引寻求好。将转换应用于每个日期字段,这是不好的。文化特定日期常数。最复杂的查询计划。

-- 5 - Explicit conversion, still sargable
--     applies conversion to each date field
SELECT COUNT(*) 
FROM Test2 
WHERE CAST(my_dt AS date) >= '2000-02-01' 
AND CAST(my_dt AS date) < '2000-02-02'
OPTION (RECOMPILE, QUERYTRACEON 8607);

enter image description here

enter image description here

总之,使用解决方案4,它利用了索引,而不是文化特定的。

使用cast()不是一个好建议。它使用索引,但在比较期间花费了额外的时间来转换每个索引值。

请注意自己,请务必详细解释我的意思。

以下是关于该主题的一些好读物!

参考资料 - 所有关于日期。

http://karaszi.com/the-ultimate-guide-to-the-datetime-datatypes

参考 - 亚伦对日期使用的建议。

https://sqlblog.org/2009/10/16/bad-habits-to-kick-mis-handling-date-range-queries

什么是SARGABLE。

http://en.wikipedia.org/wiki/Sargable

答案 2 :(得分:-1)

使用这种经过验证的方法将日期时间组件“归零”:

select MyDateTimeColumn
from MyTable
where DATEADD(dd, DATEDIFF(dd, 0, MyDateTimeColumn), 0) = CONVERT(date, '07-09-2014', 110)

如果有更快的方法(如上面的技巧),请避免转换为不同的日期类型,并且绝对尽量避免在没有将它们包装在CONVERT()中的情况下使用字符串文字来确保正确解释字符串格式。 / p>

如果您有性能问题并希望强制它使用索引,我建议添加类型为date的列(用现有列的值减去时间部分填充它),并索引/搜索,或创建一个完成相同的索引视图。