硬编码日期字符串比SELECT中的DateTime快得多?

enf*_*rge 1 sql sql-server indexing performance

我有一个非常大的表(1500万行,这是一个审计表).

我需要运行一个查询,检查审计表中某个日期之后的事件并满足某些条件(我正在寻找仅在当天发生的审计记录)

当我跑:

SELECT Field1, Field2 FROM AUDIT_TABLE WHERE AUDIT_DATE >= '8/9/12'
Run Code Online (Sandbox Code Playgroud)

结果回来相当快(几秒钟,15M行不错)

当我跑:

SELECT Field1, Field2 FROM AUDIT_TABLE WHERE AUDIT_DATE >= @DateTime
Run Code Online (Sandbox Code Playgroud)

它需要11-15秒并进行全表扫描.

我查询的实际字段是DATETIME类型,索引也在该字段上.

Aar*_*and 7

听起来好像你被困在一个糟糕的计划中,可能是因为有人在某个时刻使用了一个参数,选择了足够多的表,表扫描是该参数值的最有效方式.尝试以这种方式运行查询:

SELECT ... FROM AUDIT_TABLE WHERE AUDIT_DATE >= @DateTIme OPTION (RECOMPILE);
Run Code Online (Sandbox Code Playgroud)

然后以这种方式更改代码:

SELECT ... FROM dbo.AUDIT_TABLE WHERE AUDIT_DATE >= @DateTime;
Run Code Online (Sandbox Code Playgroud)

使用dbo.前缀将至少阻止具有不同模式的不同用户使用不同版本的计划污染计划缓存.它还将取消未来查询与存储的错误计划的关联.

如果你要在选择最近的行(小%)和很多行之间有所不同,我可能会在那里留下OPTION(RECOMPILE).每次重新编译时支付较小的CPU损失比在大多数查询中遇到错误的计划要便宜.

我见过的另一个技巧是用来绕过参数嗅探:

ALTER PROCEDURE dbo.whatever
  @DateTime DATETIME
AS
BEGIN
  SET NOCOUNT ON;

  DECLARE @dt DATETIME;
  SET @dt = @DateTime;

  SELECT ... WHERE AUDIT_DATE >= @dt;
END
GO
Run Code Online (Sandbox Code Playgroud)

这是一种肮脏且不直观的技巧,但它可以让优化器更好地了解参数值,并为优化该值提供更好的机会.