我在大约1000万行的大桌子上进行搜索。我想指定一个开始和结束日期,并返回在这些日期之间创建的表中的所有记录。
这是一个直截了当的问题:
declare @StartDateTime datetime = '2016-06-21',
@EndDateTime datetime = '2016-06-22';
select *
FROM Archive.dbo.Order O WITH (NOLOCK)
where O.Created >= @StartDateTime
AND O.Created < @EndDateTime;
Created是一个DATETIME列,它具有非聚集索引。
此查询大约需要15秒才能完成。
但是,如果稍微修改查询,如下所示,返回相同的结果只需1秒钟:
declare @StartDateTime datetime = '2016-06-21',
@EndDateTime datetime = '2016-06-22';
select *
FROM Archive.dbo.Order O WITH (NOLOCK)
where O.Created >= '2016-06-21'
AND O.Created < @EndDateTime;
唯一的变化是用字符串文字替换@StartDateTime
搜索谓词。查看执行计划,当我使用@StartDateTime
时,它执行了索引扫描,但是当我使用字符串文字时,它执行索引搜索并且速度提高了15倍。
有谁知道为什么使用字符串文字要快得多?
我原本以为在DATETIME列和DATETIME变量之间进行比较会比将列与日期的字符串表示形式进行比较更快。我已尝试在Created列上删除并重新创建索引,但没有任何区别。我注意到我在生产系统上得到的结果与在测试系统上得到的结果相似,因此奇怪的行为似乎并不特定于特定的数据库或SQL Server实例。
答案 0 :(得分:2)
所有变量都具有可识别的实例。
在OOP
种语言中,我们通常使用关键字来区分临时变量中的static
/ constant
变量,或者当变量被调用到函数中时,变量在该实例内部如果函数转换该变量,则将其视为常量,如C++
中的以下内容:
void string MyFunction(string& name)
//technically, `&` calls the actual location of the variable
//instead of using a logical representation. The concept is the same.
在SQL Server
中,标准选择以不同的方式实施它。没有constant
数据类型,因此我们使用
[]
)CHAR(39)
(')。这就是你注意到这两个查询产生不同结果的原因,因为这些变量不是优化器的常量,这意味着SQL Server
已经预先选择了它的执行路径。
如果安装了SSMS,请包含实际执行计划(CTRL + M),并在select语句中注意Estimated Rows是什么。这是执行计划的重点。 Estimated和Actual行之间的差异越大,查询就越有可能使用优化。在您的示例中,SQL Server必须猜测行数,并最终超出结果,从而失去效率。
解决方案是同一个,但如果你愿意,你仍然可以封装所有内容。我们在此示例中使用AdventureWorks2012
:
1)在Procedure
CREATE PROC dbo.TEST1 (@NameStyle INT, @FirstName VARCHAR(50) )
AS
BEGIN
SELECT *
FROM Person.PErson
WHERE FirstName = @FirstName
AND NameStyle = @NameStyle; --namestyle is 0
END
2)将变量传递给Dynamic SQL
CREATE PROC dbo.TEST2 (@NameStyle INT)
AS
BEGIN
DECLARE @Name NVARCHAR(50) = N'Ken';
DECLARE @String NVARCHAR(MAX)
SET @String =
N'SELECT *
FROM Person.PErson
WHERE FirstName = @Other
AND NameStyle = @NameStyle';
EXEC sp_executesql @String
, N'@Other VARCHAR(50), @NameStyle INT'
, @Other = @Name
, @NameStyle = @NameStyle
END
两个计划都会产生相同的结果。我本身可以使用EXEC
,但sp_executesql
可以缓存整个select语句(加上,SQL Injection
更安全
注意在两种情况下,实例的级别如何允许SQL Server
将变量转换为常量值(意味着它以设定值输入对象),然后优化器能够选择最有效的执行计划可用。
-- Remove Procs
DROP PROC dbo.TEST1
DROP PROC dbo.TEST2
OP的评论部分突出了一篇很棒的文章,但你可以在这里看到:Optimizing Variables and Parameters - SQLMAG