需要有关SQL Server 2005上SQL查询的帮助吗
在SQL Server 2005上运行两个版本的查询时,我们会看到奇怪的行为: A版:需要有关SQL Server 2005上SQL查询的帮助吗,sql,sql-server-2005,nhibernate,Sql,Sql Server 2005,Nhibernate,在SQL Server 2005上运行两个版本的查询时,我们会看到奇怪的行为: A版: SELECT otherattributes.* FROM listcontacts JOIN otherattributes ON listcontacts.contactId = otherattributes.contactId WHERE listcontacts.listid = 1234 ORDER BY name ASC B版: DECLARE @Id AS INT; SET @Id = 12
SELECT otherattributes.* FROM listcontacts JOIN otherattributes
ON listcontacts.contactId = otherattributes.contactId WHERE listcontacts.listid = 1234
ORDER BY name ASC
B版:
DECLARE @Id AS INT;
SET @Id = 1234;
SELECT otherattributes.* FROM listcontacts JOIN otherattributes
ON listcontacts.contactId = otherattributes.contactId
WHERE listcontacts.listid = @Id
ORDER BY name ASC
两个查询都返回1000行;A版平均需要15秒;B版平均需要4秒。
有人能帮助我们理解这两个版本的SQL在执行时间上的差异吗
如果我们使用NHibernate通过命名参数调用此查询,我们将通过SQL Server profiler看到以下查询:
EXEC sp_executesql N'SELECT otherattributes.* FROM listcontacts JOIN otherattributes ON listcontacts.contactId = otherattributes.contactId WHERE listcontacts.listid = @id ORDER BY name ASC',
N'@id INT',
@id=1234;
…这往往会与版本A一样糟糕。请查看查询的执行计划。这将为您提供有关如何执行查询的更多解释。请尝试查看查询的执行计划。这将为您提供有关如何执行查询的更多解释。可能不是将1234强制转换为与listcontacts.listid相同的类型,然后对每一行进行比较,而是将每一行中的值强制转换为与1234相同的值。第一个只需要一次强制转换,第二个需要每行强制转换一次(可能远远超过1000行,可能是表中的每一行)。我不确定该常量将被解释为什么类型,但它可能是“数值”而不是“int” 如果这是原因,那么第二个版本会更快,因为它强制将1234解释为int,从而消除了在每一行中强制转换值的需要
但是,如前一张海报所示,SQL Server Management Studio中显示的查询计划可能表明另一种解释。可能不是将1234转换为与listcontacts.listid相同的类型,然后对每行进行比较,而是将每行中的值转换为与1234相同的值。第一个只需要一次强制转换,第二个需要每行强制转换一次(可能远远超过1000行,可能是表中的每一行)。我不确定该常量将被解释为什么类型,但它可能是“数值”而不是“int” 如果这是原因,那么第二个版本会更快,因为它强制将1234解释为int,从而消除了在每一行中强制转换值的需要
但是,正如前一张海报所示,SQL Server Management Studio中显示的查询计划可能表明了另一种解释。查看正在发生的情况的最佳方法是比较执行计划,其他一切都是基于问题中提供的有限细节的推测
要查看执行计划,请进入SQL Server Management Studio并运行
SET SHOWPLAN_XML ON
,然后运行查询版本A,查询将不会运行,但执行计划将以XML显示。然后运行查询版本B并查看其执行计划。如果您仍然无法分辨差异或解决问题,请发布两个执行计划,这里会有人对此进行解释。了解情况的最佳方法是比较执行计划,其他一切都是基于问题中有限细节的推测
要查看执行计划,请进入SQL Server Management Studio并运行
SET SHOWPLAN_XML ON
,然后运行查询版本A,查询将不会运行,但执行计划将以XML显示。然后运行查询版本B并查看其执行计划。如果您仍然无法分辨差异或解决问题,请发布这两个执行计划,这里会有人解释。我没有看到执行计划,但我强烈怀疑这两种情况下的执行计划是不同的。您所面临的问题是,在A(更快的查询)情况下,优化程序知道您用于列表id(1234)的值,并使用分布统计信息和索引的组合选择最佳计划
在第二种情况下,优化者无法嗅探ID的值,因此生成一个计划,该计划对于任何传入的列表ID都是可接受的。我所说的可接受并不意味着最优
那么,您可以做些什么来改进场景呢?这里有两种选择:
1) 创建存储过程以执行以下查询:
创建程序Foo
@Id INT
像
选择otherattributes.*从列表联系人加入otherattributes
在listcontacts.contactId=otherattributes.contactId上,其中listcontacts.listid=@Id
按名称订购ASC
去
这将允许优化人员在传入时嗅探输入参数的值,并为第一次执行生成适当的执行计划。不幸的是,它将缓存该计划以供以后重用,因此除非您通常使用类似的选择性值调用存储过程,否则这可能不会对您有太大帮助
2) 如上所述创建存储过程,但将其指定为带有重新编译。这将确保存储过程每次执行时都会重新编译,从而生成针对该输入值优化的新计划
3) 将选项(重新编译)添加到SQL语句的末尾。强制重新编译此语句,并能够优化输入值
4) 在SQL语句的末尾添加选项(优化(@Id=1234))。这将导致缓存的计划为此特定输入值进行优化。如果这是一个非常常见的值,或者大多数常见的值具有类似的选择性,那就太好了,但是如果选择性分布更广泛,就不太好了。我没有看到执行计划,但我强烈怀疑这两种情况下的执行计划是不同的。您所面临的问题是,在A(更快的查询)情况下,优化程序知道您用于列表id(1234)的值,并使用分布统计信息和索引的组合选择最佳计划 在第二种情况下,优化者无法嗅探ID的值,因此生成的计划对于任何传入的列表ID都是可接受的。我说的可接受并不意味着最优。