在 SQL Server 2008中添加了日期数据类型。
将datetime列转换date为sargable并且可以在datetime列上使用索引。
select *
from T
where cast(DateTimeCol as date) = '20130101';
Run Code Online (Sandbox Code Playgroud)
您拥有的另一个选择是改用范围。
select *
from T
where DateTimeCol >= '20130101' and
DateTimeCol < '20130102'
Run Code Online (Sandbox Code Playgroud)
这些查询是同样好还是应该优先于另一个?
Mar*_*ith 70
迄今为止,可转换性背后的机制称为动态查找。
SQL Server 调用内部函数GetRangeThroughConvert来获取范围的开始和结束。
出人意料的是,这是不相同的范围内为您的文字值。
创建一个每页一行,每天 1440 行的表格
CREATE TABLE T
(
DateTimeCol DATETIME PRIMARY KEY,
Filler CHAR(8000) DEFAULT 'X'
);
WITH Nums(Num)
AS (SELECT number
FROM spt_values
WHERE type = 'P'
AND number BETWEEN 1 AND 1440),
Dates(Date)
AS (SELECT {d '2012-12-30'} UNION ALL
SELECT {d '2012-12-31'} UNION ALL
SELECT {d '2013-01-01'} UNION ALL
SELECT {d '2013-01-02'} UNION ALL
SELECT {d '2013-01-03'})
INSERT INTO T
(DateTimeCol)
SELECT DISTINCT DATEADD(MINUTE, Num, Date)
FROM Nums,
Dates
Run Code Online (Sandbox Code Playgroud)
然后运行
SET STATISTICS IO ON;
SET STATISTICS TIME ON;
SELECT *
FROM T
WHERE DateTimeCol >= '20130101'
AND DateTimeCol < '20130102'
SELECT *
FROM T
WHERE CAST(DateTimeCol AS DATE) = '20130101';
Run Code Online (Sandbox Code Playgroud)
第一个查询有1443读取,第二个查询读取2883整整一天,然后根据剩余谓词丢弃它。
该计划显示寻求谓词是
Seek Keys[1]: Start: DateTimeCol > Scalar Operator([Expr1006]),
End: DateTimeCol < Scalar Operator([Expr1007])
Run Code Online (Sandbox Code Playgroud)
因此,而不是>= '20130101' ... < '20130102'读取> '20121231' ... < '20130102'然后丢弃所有2012-12-31行。
依赖它的另一个缺点是基数估计可能不如传统范围查询准确。这可以在SQL Fiddle的修改版本中看到。
表中的所有 100 行现在都匹配谓词(日期时间都在同一天相隔 1 分钟)。
第二个(范围)查询正确估计 100 将匹配并使用聚集索引扫描。该CAST( AS DATE)查询错误地估计,只有一行将匹配并产生与键查找的计划。
统计数据并未被完全忽略。如果表中的所有行都相同datetime并且匹配谓词(例如20130101 00:00:00or 20130101 01:00:00),则计划显示聚集索引扫描,估计有 31.6228 行。
100 ^ 0.75 = 31.6228
Run Code Online (Sandbox Code Playgroud)
所以在这种情况下,估计值似乎是从这个公式得出的:
下表显示了猜测的合词数和作为 N 的输入表基数函数的结果选择性:
Run Code Online (Sandbox Code Playgroud)CREATE TABLE T ( DateTimeCol DATETIME PRIMARY KEY, Filler CHAR(8000) DEFAULT 'X' ); WITH Nums(Num) AS (SELECT number FROM spt_values WHERE type = 'P' AND number BETWEEN 1 AND 1440), Dates(Date) AS (SELECT {d '2012-12-30'} UNION ALL SELECT {d '2012-12-31'} UNION ALL SELECT {d '2013-01-01'} UNION ALL SELECT {d '2013-01-02'} UNION ALL SELECT {d '2013-01-03'}) INSERT INTO T (DateTimeCol) SELECT DISTINCT DATEADD(MINUTE, Num, Date) FROM Nums, Dates
如果表中的所有行都相同datetime并且它不匹配谓词(例如20130102 01:00:00),那么它会回退到估计的行数 1 和带有查找的计划。
对于表具有多个DISTINCT值的情况,估计的行似乎与查询正在寻找的行完全相同20130101 00:00:00。
如果统计直方图恰好有一个步骤,2013-01-01 00:00:00.000那么估计将基于EQ_ROWS(即不考虑该日期的其他时间)。否则,如果没有步骤,它看起来好像使用AVG_RANGE_ROWS来自周围步骤的 。
由于datetime在许多系统中具有大约 3ms 的精度,因此实际重复值很少,这个数字将为 1。
Eri*_*ing 21
我知道这有来自 Martin 的长久以来的 Great Answer®,但我想在较新版本的 SQL Server 中对此处的行为进行一些更改。这似乎只测试到 2008R2。
使用新的USE HINT可以进行一些基数估计时间旅行,我们可以看到情况何时发生变化。
使用与 SQL Fiddle 相同的设置。
CREATE TABLE T ( ID INT IDENTITY PRIMARY KEY, DateTimeCol DATETIME, Filler CHAR(8000) NULL );
CREATE INDEX IX_T_DateTimeCol ON T ( DateTimeCol );
WITH E00(N) AS (SELECT 1 UNION ALL SELECT 1),
E02(N) AS (SELECT 1 FROM E00 a, E00 b),
E04(N) AS (SELECT 1 FROM E02 a, E02 b),
E08(N) AS (SELECT 1 FROM E04 a, E04 b),
Num(N) AS (SELECT ROW_NUMBER() OVER (ORDER BY E08.N) FROM E08)
INSERT INTO T(DateTimeCol)
SELECT TOP 100 DATEADD(MINUTE, Num.N, '20130101')
FROM Num;
Run Code Online (Sandbox Code Playgroud)
我们可以像这样测试不同的级别:
SELECT *
FROM T
WHERE CAST(DateTimeCol AS DATE) = '20130101'
OPTION ( USE HINT ( 'QUERY_OPTIMIZER_COMPATIBILITY_LEVEL_100' ));
GO
SELECT *
FROM T
WHERE CAST(DateTimeCol AS DATE) = '20130101'
OPTION ( USE HINT ( 'QUERY_OPTIMIZER_COMPATIBILITY_LEVEL_110' ));
GO
SELECT *
FROM T
WHERE CAST(DateTimeCol AS DATE) = '20130101'
OPTION ( USE HINT ( 'QUERY_OPTIMIZER_COMPATIBILITY_LEVEL_120' ));
GO
SELECT *
FROM T
WHERE CAST(DateTimeCol AS DATE) = '20130101'
OPTION ( USE HINT ( 'QUERY_OPTIMIZER_COMPATIBILITY_LEVEL_130' ));
GO
SELECT *
FROM T
WHERE CAST(DateTimeCol AS DATE) = '20130101'
OPTION ( USE HINT ( 'QUERY_OPTIMIZER_COMPATIBILITY_LEVEL_140' ));
GO
SELECT *
FROM T
WHERE CAST(DateTimeCol AS DATE) = '20130101'
OPTION ( USE HINT ( 'QUERY_OPTIMIZER_COMPATIBILITY_LEVEL_150' ));
GO
Run Code Online (Sandbox Code Playgroud)
所有这些的计划都可以在这里找到。兼容级别 100 和 110 都给出了键查找计划,但从兼容级别 120 开始,我们开始获得具有 100 行估计值的相同扫描计划。这在兼容级别 150 之前都是正确的。
>= '20130101', < '20130102'计划的基数估计仍为 100,这是预期的。