相同 SQL 语句的执行计划不同

Tim*_*Tim 4 sql-server t-sql execution-plan sql-server-2008-r2 sql-server-2012

作为此问题的背景,可以在此链接中找到详细信息。

简而言之:除了RIGHT表之外,我有 2 个几乎相同的 SQL 语句。该LEFT JOIN是相同的,并且返回的列不同,因为该表是不同的。数据库从 2008 年移至 SQL Server 2012 实例。在 2012 实例上,1 个查询在 3 秒内执行,而另一个则需要将近 2 小时才能执行。执行计划有所不同,并发布在链接上。

但是,我可以接受这两个查询并在 SQL Server 2008 服务器上执行它们,它们都在 3 秒内完成。2008 服务器上的两个执行计划与 2012 服务器上的“良好”执行计划相同。

我有一个可以工作的新语句,但它所使用的应用程序是一个使用内联 SQL 的 C# windows 应用程序。我不想打破这个 1 语句的模式。任何人都可以阐明这个问题吗?

Pau*_*ite 13

为该查询构建执行计划时,查询优化器有多种选择。在众多可用的策略中,它可以在散列连接和嵌套循环连接之间进行选择。它决定使用哪一个敏感地取决于可用的统计信息,以及其他因素,例如为 SQL Server 配置的内存量。

碰巧的是,优化器在一种情况下选择嵌套循环策略,而在其他情况下选择散列连接。如果您要强制使用散列连接(例如,OPTION (HASH JOIN)在当前使用嵌套循环连接的情况下使用查询提示,您会发现嵌套循环计划的估计成本似乎是优化器更便宜的选项。

这不是一个错误。这是计划选择对可用统计信息(除其他外)敏感的一个相当常规的例子。嵌套循环联接在现实中表现如此糟糕的事实是查询和数据库设计对优化器不太友好的结果。考虑到要处理的质量非常低的信息,优化器的计划选择几乎不比猜测好。

无论如何,假设您需要在不更改源代码的情况下避免性能不佳的计划形状(顺便说一下,这是首选存储过程而不是内联 SQL 的原因),您有两个主要选择:

第一个是使用计划指南来强制目标查询的“良好”计划形状。如果您以前没有使用过计划指南,这是相当先进的工作。如果示例中指定的文字值可能不同,则将涉及额外的步骤。

第二种选择是向优化器提供更有用的索引以供使用。在这种情况下,这涉及添加一个计算列(一个快速的仅元数据操作),然后索引新列:

-- Metadata-only operation
ALTER TABLE dbo.InHouse_CSV_Backup
ADD MERSNUMBER_CC AS 
    REPLACE(LTRIM(RTRIM([MERSNUMBER])),'-', '');

-- Index on computed column
CREATE NONCLUSTERED INDEX
    IX_dbo_InHouse_CSV_Backup__MERSNUMBER_CC
ON dbo.InHouse_CSV_Backup (MERSNUMBER_CC)
INCLUDE (MERSNUMBER);
Run Code Online (Sandbox Code Playgroud)

查询很可能会使用索引,从而导致更好的计划稳定性和很可能的良好性能。无论如何,这都不是一个完美的解决方案,但鉴于可用的信息,它是一个相对简单且不引人注目的解决方案。

未来改进的潜在领域: