首页 > 解决方案 > SQL Server 2016 奇怪的行为 - OR 条件给出 0 行但 AND 条件给出了一些行

问题描述

我有以下 SQL 查询:

SELECT T.tnum,
       T.secId,           
FROM   TradeCore T
       INNER JOIN Sec S
               ON S.secId = T.secId
       INNER JOIN TradeTransfer TT
               ON t.tnum = TT.tnum
WHERE  ( T.td >= '2019-01-01' )
       AND ( T.td <= '2019-02-25' )
       AND ( T.fundId = 3 OR TT.fundId = 3 )
       AND ( T.stratId = 7 OR TT.stratId = 7 ) --Line 1
    -- AND ( T.stratId = 7 AND TT.stratId = 7 ) --Line 2

当我保留最后一行评论时,我得到 0 个结果,但是当我取消评论它并评论它之前的行时,我得到了一些结果。

这怎么可能?

标签: sql-servertsql

解决方案


任何行会议都(T.stratId = 7 AND TT.stratId = 7)必须满足(T.stratId = 7 OR TT.stratId = 7),因此限制较少的谓词返回较少的结果在逻辑上是不可能的。

问题是损坏的非聚集索引。

和案例

  • TradeCore匹配日期条件和stratId = 7的154行被发出。
  • 加入TradeTransfer并应用stratId条件fundId输出 68 行(估计 34 行)
  • 这些都成功加入Sec(使用索引 IX_Sec_secId_sectype_Ccy_valpoint)中的一行,并返回 68 行作为最终结果。

在此处输入图像描述

或案例

  • 发出1173 行TradeCore匹配日期条件
  • 加入TradeTransfer残差谓词 on3 in (T.fundId, TT.fundId) AND 7 in (T.stratId, TT.stratId)将其减少到 73(估计为 297 行)
  • 然后通过 Sec 上的连接消除所有行 - 尽管我们从上面知道其中至少有 68 个有匹配项。

在此处输入图像描述

的表基数Sec2399行。在连接删除所有行的计划中,SQL Server 对IX_Sec_idu作为散列连接的探测端的输入进行完全扫描,但对该索引的完全扫描仅返回 589 行

另一个执行计划中出现的行是从包含这 1,810 个缺失行的不同索引中提取的。

您已在评论中确认以下返回不同的结果

select count(*) from Sec with(index = IX_Sec_idul); --589 
select count(*) from Sec with(index = IX_Sec_secId_sectype_Ccy_valpoint); --2399
select count(*) from Sec with(index = PK_Sec)  --2399

这绝不应该是来自同一个表上不同索引的行数不匹配的情况(除非索引被过滤并且在此处不适用)。

不同指标的原因

Sec因为在这种情况下进入连接的行估计AND只有 34,它选择了一个带有嵌套循环的计划,因此需要一个带有前导列的索引secId来执行查找。对于这种OR情况,它估计有 297 行,而不是进行估计的 297 次搜索,而是选择散列连接,因此选择包含该secId列的可用最小索引。

使固定

由于聚集索引中存在所有行,您可以删除IX_Sec_idul并再次创建它以希望解决此问题(首先进行备份)。

您还应该运行dbcc checkdb以查看是否存在任何其他问题。


推荐阅读