迄今为止的演员阵容是可以讨论的,但这是一个好主意吗?

Mik*_*son 51 sql-server

在 SQL Server 2008中添加了日期数据类型。

datetime列转换datesargable并且可以在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 的输入表基数函数的结果选择性:

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)

如果表中的所有行都相同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。

  • @TT。我认为关键是这不是一个好主意。为什么要使用需要备忘单的方法? (7认同)
  • 嗨,马丁,你能否添加一个“TL;DR”部分,其中包含一些不同案例的要点,添加在这种情况下,迄今为止的演员阵容是否是一个好主意? (2认同)

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,这是预期的。