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

Iva*_*nho 2 t-sql sql-server performance casting where

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

SELECT MyDateTimeColumn 
FROM MyTable
WHERE CAST(MyDateTimeColumn AS DATE) = '2014-07-09'
Run Code Online (Sandbox Code Playgroud)

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

Ano*_*non 13

在SQL 2008+中,CAST(foo AS date)可以使用其他一些操作.查看sqlfiddle中的执行计划.

SQL小提琴演示


CRA*_*DBA 5

所以,让我们使用 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
Run Code Online (Sandbox Code Playgroud)

现在,让我们来看看每个解决方案。利弊。

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

解决方案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)
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

在此处输入图片说明

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

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

在此处输入图片说明

在此处输入图片说明

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

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

在此处输入图片说明

在此处输入图片说明

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

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

在此处输入图片说明

在此处输入图片说明

解决方案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);
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

在此处输入图片说明

总之,使用解决方案 4,它利用了索引并且不特定于文化。

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

自我注意,确保我详细解释了我的意思。

这里有一些关于这个主题的好读物!

参考资料 - 关于日期。

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

参考 - Aaron 关于日期使用的建议。

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

什么是SARGABLE。

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