问题:
将DateTime.Now
作为参数传递给proc会阻止SQL Server缓存查询计划吗?如果是这样,那么网络应用程序是否错过了巨大的性能提升?
可能的解决方案:
我认为DateTime.Today.AddDays(1)
可能是一种解决方案。它会将相同的结束日期传递给sql proc(每天)。用户仍然可以获得最新数据。请同意这一点。
举例:
假设我们有一个存储过程。它将数据报告给网页上的用户。用户可以设置日期范围。如果用户将今天的日期设置为“结束日期”(包括今天的数据),则Web应用程序会将DateTime.Now
传递给sql proc。
假设有一个用户多次运行报告 - 5/1/2010
到now
。在网页上,用户会看到5/1/2010
到5/4/2010
。但是,Web应用程序将DateTime.Now
作为结束日期传递给sql proc。因此,虽然用户正在查询类似的日期范围,但proc中的结束日期将始终不同。
假设表中的记录数和用户数都很大。所以任何性能提升都很重要因此问题的重要性。
示例proc和执行(如果这有助于理解):
CREATE PROCEDURE GetFooData
@StartDate datetime
@EndDate datetime
AS
SELECT *
FROM Foo
WHERE LogDate >= @StartDate
AND LogDate < @EndDate
这是使用DateTime.Now的示例执行:
EXEC GetFooData '2010-05-01', '2010-05-04 15:41:27' -- passed in DateTime.Now
这是使用DateTime.Today.AddDays(1)
的示例执行EXEC GetFooData '2010-05-01', '2010-05-05' -- passed in DateTime.Today.AddDays(1)
两个过程都返回相同的数据,因为当前时间是:2010-05-04 15:41:27
。
答案 0 :(得分:6)
无论参数值如何,都将缓存查询计划。参数基本上保证存在一致的,可重用的查询,因为就SQL服务器而言它们是类型安全的。
您想要的不是查询计划,而是结果缓存。这将受到您描述的行为的影响。
由于您似乎只处理整天,您可以尝试传递日期,而不是日期时间,以最小化不同的参数值。还尝试在应用程序中缓存查询结果,而不是每次都进行数据库往返。
答案 1 :(得分:1)
因为您调用存储过程而不是直接调用查询,所以您更改的唯一查询是您发送给SQL的实际批处理EXEC GetFooData '2010-05-01', '2010-05-05'
与GetFooData '2010-05-01', '2010-05-04 15:41:27'
。这是一个微不足道的批次,将产生一个简单的计划。诚然,从严格的技术角度来看,你正在失去一些表现,但这几乎是不可测量的。此响应中解释了发生这种情况的详细信息:Dynamically created SQL vs Parameters in SQL Server
好消息是,通过对SqlClient调用代码进行微小更改,即使是那里提到的微小性能改进,您也会受益。将您的SqlCommand代码更改为显式存储过程调用:
SqlCommand cmd = new SqlCommand("GetFooData", connection);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("@StartDate", dateFrom);
cmd.Parameters.AddWithValue("@EndDate", DateTime.Now);
作为旁注,在数据库中存储本地化时间不是一个好主意,因为客户端位于与服务器不同的时区,并且由于夏令时改变夜晚的复杂性。一个更好的解决方案是始终存储UTC时间,并将其格式化为用户在应用程序中的本地时间。
答案 2 :(得分:1)
在你的情况下,如果第二个参数只是实时向上漂移,你可能会很好。
然而,有可能成为parameter sniffing的受害者,其中第一次执行(产生缓存的执行计划)被调用参数,这些参数产生的计划通常不适合通常使用的其他参数(或者数据配置文件发生了巨大变化)。后来的调用可能会使用一个有时很差的计划,甚至无法正常完成。
如果您的数据配置文件因参数的不同选择而急剧变化,并且某些参数选择的执行计划变得很差,您可以将参数屏蔽到本地变量中 - 这将有效地防止SQL Server 2005中的参数嗅探。还有WITH RECOMPILE(在SP或EXEC中 - 但对于大量调用的SP,这不是一个可行的选项)在SQL Server 2008中,我几乎总是使用OPTIMIZE FOR UNKNOWN,这将避免基于参数嗅探生成计划