使用这样的表结构检索日期范围的最有效方法是什么?
create table SomeDateTable
(
id int identity(1, 1) not null,
StartDate datetime not null,
EndDate datetime not null
)
go
假设您想要StartDate
和的范围EndDate
。所以换句话说,如果StartDate
落在 and 之间@StartDateBegin
,@StartDateEnd
并且EndDate
落在 and 之间@EndDateBegin
,@EndDateEnd
那么做一些事情。
我知道有几种方法可以解决这个问题,但最建议的是什么?
一般来说,这是一个很难解决的问题,但我们可以做一些事情来帮助优化器选择一个计划。此脚本创建一个包含 10,000 行的表,其中行的已知伪随机分布来说明:
第一个问题是如何索引这个表。一种选择是在列上提供两个索引
DATETIME
,因此优化器至少可以选择是否在StartDate
或上查找EndDate
。自然,两者的不等式
StartDate
意味着EndDate
每个索引中只有一列可以支持示例查询中的查找,但这是我们能做的最好的事情。我们可能会考虑将每个索引中的第二列设置为一个INCLUDE
而不是一个键,但我们可能有其他查询可以在前导列上执行相等搜索并在第二列上执行不等式搜索。此外,我们可以通过这种方式获得更好的统计数据。反正...此查询使用变量,因此通常优化器会猜测选择性和分布,从而得到81 行的猜测基数估计。事实上,查询产生了 2076 行,这在更复杂的示例中可能很重要。
在 SQL Server 2008 SP1 CU5 或更高版本(或 R2 RTM CU1)上,我们可以利用参数嵌入优化来获得更好的估计,只需添加
OPTION (RECOMPILE)
到SELECT
上面的查询。这会导致在批处理执行之前进行编译,从而使 SQL Server 可以“查看”实际参数值并针对这些值进行优化。通过此更改,估计值提高到468 行(尽管您确实需要检查运行时计划才能看到这一点)。这个估计比 81 行要好,但仍然没有那么接近。跟踪标志 2301启用的建模扩展在某些情况下可能会有所帮助,但不适用于此查询。问题是两个范围搜索限定的行重叠的地方。在优化器的成本计算和基数估计组件中所做的简化假设之一是谓词是独立的(因此,如果两者都有 50% 的选择性,则假设应用两者的结果符合 50% 的 50% = 25% 的行)。如果这种相关性存在问题,我们通常可以使用多列和/或过滤统计信息来解决它。对于具有未知起点和终点的两个范围,这变得不切实际。这就是我们有时不得不求助于将查询重写为恰好产生更好估计的形式的地方:
这种形式恰好产生 2110 行的运行时估计值(实际为 2076 行)。除非您启用了 TF 2301,在这种情况下,更高级的建模技术会看穿这个技巧并产生与以前完全相同的估计值:468 行。
有一天,SQL Server 可能会获得对时间间隔的原生支持。如果这带有良好的统计支持,开发人员可能会害怕调优这样的查询计划。
我不知道一种对所有数据分布都快速的解决方案,但如果您的所有范围都很短,我们通常可以加快速度。例如,如果范围短于 1 天,则不要使用此查询:
我们可以再添加一个条件:
因此,查询将只扫描两天的范围,而不是扫描整个表,这样更快。如果范围可能更长,我们可以将它们存储为更短的序列。此处的详细信息:在约束的帮助下调整 SQL 查询