查询在 SQL 2005 与 SQL 2008R2 上的运行方式不同

Rat*_*rol 9 sql-server-2005 sql-server-2008

在我的办公室,我们有一个非常难看的查询,但在生产和开发环境中运行得很好(分别为 20 秒和 4 秒)。然而,在我们的测试环境中,它需要 4 小时以上。SQL2005(+最新补丁)正在生产和开发中运行。SQL2008R2 正在测试中运行。

我查看了查询计划,它表明 SQL2008R2 正在使用 TempDB,通过表假脱机(懒惰假脱机)来存储从链接服务器返回的行。下一步是显示嵌套循环(左反半连接)占用了 96.3% 的查询。两个运营商之间的界限是 5,398MB!

SQL 2005 的查询计划显示没有使用 tempdb,也没有使用 Left Anti Semi Join。

下面是经过消毒的代码和执行计划,上面是 2005 计划,下面是 2008R2。

是什么导致了急剧放缓和变化?我期待看到不同的执行计划,所以这不会打扰我。查询时间的显着减慢让我感到困扰。

我是否必须查看底层硬件,因为2008R2版本使用的是tempdb,我必须看看如何优化它的使用?

有没有更好的方法来编写查询?

谢谢您的帮助。

    INSERT INTO Table1_GroupLock (iGroupID, dLockedDate)
SELECT 
 Table1.iGroupID,
 GETDATE()
FROM Table1
WHERE 
 NOT EXISTS (
  SELECT 1
  FROM LinkedServer.Database.Table2 Alias2
  WHERE 
   (
    Alias2.FirstName + Alias2.LastName = dbo.fnRemoveNonLetter(Table1.FullName)
    AND NOT dbo.fnRemoveNonLetter(Table1.FullName) IS NULL
    AND NOT Alias2.FirstName IS NULL 
    AND NOT Alias2.LastName  IS NULL
   ) OR (
    Alias2.FamilyName = dbo.fnRemoveNonLetter(Table1.FamilyName)
    AND Alias2.Child1Name = dbo.fnRemoveNonLetter(Table1.Child1Name)
    AND NOT dbo.fnRemoveNonLetter(Table1.FamilyName) IS NULL
    AND NOT dbo.fnRemoveNonLetter(Table1.Child1Name) IS NULL
    AND NOT Alias2.Familyname IS NULL
    AND NOT Alias2.Child1Name IS NULL
   ) OR (
    Alias2.StepFamilyName = dbo.fnRemoveNonLetter(Table1.StepFamilyName)
    AND Alias2.StepFamilyNameChild1 = dbo.fnRemoveNonLetter(Table1.StepFamilyNameChild2)
    AND NOT Alias2.StepFamilyName IS NULL
    AND NOT Alias2.StepFamilyNameChild1 IS NULL
    AND NOT dbo.fnRemoveNonLetter(Table1.StepFamilyName) IS NULL
    AND NOT dbo.fnRemoveNonLetter(Table1.StepFamilyNameChild2) IS NULL
   )  
 ) AND NOT EXISTS (
  SELECT 1
  FROM Table3
  INNER JOIN Table4
   ON Table4.FirstNameType = Table3.FirstNameType 
  INNER JOIN table5
   ON table5.LastNameType = Table3.LastNameType 
  WHERE 
   Table3.iGroupID = Table1.iGroupID
   AND Table3.bIsClosed = 0
   AND Table4.sNameTypeConstant = 'new_lastname'
   AND table5.sFirstNameConstant = 'new_firstname'
 )
Run Code Online (Sandbox Code Playgroud)

SQL-2005


SQL2008R2

:: EDIT :: 从不同的 SQL2005 实例执行查询,与“好的”执行计划几乎相同。仍然不确定两个 2005 版本在 2008R2 链接服务器上的运行效果如何,比 2008R2 实例对 2008R2 实例的运行效果如何。

虽然我不否认代码可以使用一些工作,但如果代码是问题所在,我不会在所有试验中看到相同的执行计划吗?不管SQL版本?

:: EDIT :: 我已将 SP1 和 CU3 应用于两个 2008R2 实例,仍然没有骰子。我在链接服务器里专门设置了搭配,没有骰子。我在两个实例上都专门将我的用户 acct 的权限设置为 sysadmin,没有骰子。我还记得我的 sql server 2008 内部结构和故障排除,我们将看看我是否可以找到一些方法。

感谢大家的帮助和提示。

:: EDIT :: 我对链接服务器进行了各种权限更改。我使用过 SQL 登录、域登录、模拟用户、使用“使用此安全上下文进行制作”选项。我在链接服务器的两端都创建了在服务器上具有系统管理员权限的用户。我没有想法。

我仍然想知道为什么 SQL2005 执行的查询与 SQL2008R2 有如此大的不同。如果查询不好,我会在 SQL2005 和 SQL2008R2 上看到 4 小时以上的运行时间。

Pau*_*ite 6

添加到前面的答案中,计划回归的原因可能是由于计划包含 Anti Semi Join 时已知的基数估计错误。请参阅知识库文章 2222998

假设 2005 计划产生了可接受的性能,您可能会发现将服务器升级到包含该修复程序的版本(并启用 TF4199 以激活它)将使您返回到“良好”计划。

也就是说,还有许多其他机会可以改进该查询,因此现在可能是专注于这样做的好时机。


gbn*_*gbn 5

我建议将远程数据在本地进行假脱机处理,因为以下原因之一

  1. 链接服务器设置(如整理)不一样
  2. 尽管链接服务器设置,本地排序规则与远程排序规则不同
  3. 查询由于权限无法远程正确运行

对于第 1 点,请参见sp_serveroption
。对于第 2 点,还要检查 server/db 排序规则。

对于第 3 点,请参阅 Linchi Shea 的这些:

您要求 SQL Server 在本地处理所有数据,根据我在此处的回答: 在视图中使用 OPENQUERY 的性能影响

编辑

再看一看,我看到“好”计划中有 2 个远程调用,而不是一个。这证实了我在这里所说的


Rob*_*ley 5

我希望您重新编写查询。

您有 sargability 问题,甚至在那里使用标量函数调用,这也会损害查询。您可能希望在 Table2 上创建一个 FullName 计算列并在其上放置一个索引,确保您的索引包含 FirstName 和 LastName。您还应该添加有助于其他人的索引

此外,制作一个内联表值函数来执行“RemoveNonLetter”功能,并重新编写查询以使用它,可能像我在这里所做的那样使用 APPLY。

并且一定要检查保罗的答案 所指的那个错误。

INSERT INTO Table1_GroupLock (iGroupID, dLockedDate)
SELECT 
 Table1.iGroupID,
 GETDATE()
FROM Table1
OUTER APPLY (SELECT NonLettersRemoved FROM dbo.ifnRemoveNonLetter(Table1.FullName)) AS fn (FullName)
OUTER APPLY (SELECT NonLettersRemoved FROM dbo.ifnRemoveNonLetter(Table1.FamilyName)) AS famn (FamilyName)
OUTER APPLY (SELECT NonLettersRemoved FROM dbo.ifnRemoveNonLetter(Table1.Child1Name)) AS c1n (Child1Name)
OUTER APPLY (SELECT NonLettersRemoved FROM dbo.ifnRemoveNonLetter(Table1.StepFamilyName)) AS sfn (StepFamilyName)
OUTER APPLY (SELECT NonLettersRemoved FROM dbo.ifnRemoveNonLetter(Table1.StepFamilyNameChild2)) AS sfnc2 (StepFamilyNameChild2)
WHERE 
 NOT EXISTS (
  SELECT 1
  FROM LinkedServer.Database.Table2 Alias2
  WHERE Alias2.FullName = fn.FullName
  UNION ALL
  SELECT 1
  FROM LinkedServer.Database.Table2 Alias2
  WHERE Alias2.FamilyName = famn.FamilyName AND Alias2.Child1Name = c1n.Child1Name
  UNION ALL
  SELECT 1
  FROM LinkedServer.Database.Table2 Alias2
  WHERE Alias2.StepFamilyName = sfn.StepFamilyName AND Alias2.StepFamilyNameChild1 = sfnc2.StepFamilyNameChild2
 ) 
 AND NOT EXISTS (
  SELECT 1
  FROM Table3
  INNER JOIN Table4
   ON Table4.FirstNameType = Table3.FirstNameType 
  INNER JOIN table5
   ON table5.LastNameType = Table3.LastNameType 
  WHERE 
   Table3.iGroupID = Table1.iGroupID
   AND Table3.bIsClosed = 0
   AND Table4.sNameTypeConstant = 'new_lastname'
   AND table5.sFirstNameConstant = 'new_firstname'
 )
;
Run Code Online (Sandbox Code Playgroud)