时间:2010-07-06 19:07:56

标签: sql sql-server tsql sql-server-2008

我正在加入一个有两个记录id字段(record1,record2)的表到一个视图两次 - 每次记录一次 - 并选择前1000个。该视图由几个相当大的表组成,它是id字段是它们各自ID的字符串连接(这对于需要视图的唯一ID的某些第三方软件是必需的。行编号非常慢)。在视图中还有一个where子句,它调用一个比较日期的函数。

除非我使用OPTION(FORCE ORDER),否则估计的执行计划会产生“No Join Predicate”警告。通过强制排序,执行计划具有显示100%成本的多个节点。在这两种情况下,端点的估计子树成本比其中一个节点小13个数量级(它正在执行 lot 或嵌套循环连接,cpu成本高达35927400000000)

执行计划中的数字是怎么回事?为什么SQL Server很难优化查询?

只需在连接字符串上添加索引并使用NOEXPAND表提示就可以完全解决问题。它在12秒内完成。但是为什么sql绊倒如此糟糕(甚至在我添加索引后需要noexpand提示)?

使用CU 8运行SQL Server 2008 SP1。

视图:

SELECT
    dbo.fnGetCombinedTwoPartKey(N.NameID,A.AddressID) AS NameAddressKey,
    [other fields]

FROM     
    [7 joined tables]
WHERE dbo.fnDatesAreOverlapping(N.dtmValidStartDate,N.dtmValidEndDate,A.dtmValidStartDate,A.dtmValidEndDate) = 1

查询

SELECT TOP 1000
    vw1.strFullName,
    vw1.strAddress1,
    vw1.strCity,
    vw2.strFullName,
    vw2.strAddress1,
    vw2.strCity
FROM tblMatches M
JOIN vwImportNameAddress vw1 ON vw1.NameAddressKey = M.Record1 
JOIN vwImportNameAddress vw2 ON vw2.DetailAddressKey = M.Record2 

2 个答案:

答案 0 :(得分:1)

它必须解析您的函数(fnGetCombinedTwoPartKey)以确定获取哪些列来创建结果列。它不能这样,它会假设所有列都是必要的。如果您的索引覆盖索引,那么您的估计就会出错。

答案 1 :(得分:1)

看起来你已经非常接近这个解释了。这是因为:

  

视图由几个相当大的表组成,它的id字段是各自ID的字符串连接......

这会创建一个不可sargable的连接谓词条件,并阻止SQL Server使用基表上的任何索引。因此,引擎必须对每个连接执行所有基础表的完整扫描(在您的情况下为两个)。

也许为了避免进行多次全表扫描(每个表一个,再乘以连接数),SQL Server已经决定简单地使用笛卡尔积并在之后过滤(因此“no”加入谓词“警告”。当您FORCE ORDER时,它会尽职尽责地执行您最初要求的所有完整扫描和嵌套循环。

我同意一些评论,认为这种观点是有问题的数据模型的基础,但正如您所发现的那样,短期解决方法是在视图中索引计算出的ID列,这显然会使它再次受到攻击,因为它具有实际生成ID的哈希值。


编辑:我在第一次阅读中也错过了这个:

WHERE dbo.fnDatesAreOverlapping(N.dtmValidStartDate,N.dtmValidEndDate,A.dtmValidStartDate,A.dtmValidEndDate) = 1

这又是一个不可预测的谓词,会导致表现不佳。包装UDF中的任何列都将导致此行为。索引视图也会实现它,这也可能会影响查询的速度;如果没有索引,则必须每次都对此谓词进行求值,并强制对基表进行完整扫描,即使没有复合ID也是如此。