SQL Server 2016奇怪的行为-OR条件给出0行但AND条件给出一些行

时间:2019-03-15 13:28:58

标签: sql-server tsql

我有以下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个结果,但是当我取消注释并在它前面的行中注释时,我得到了一些结果。

这怎么可能?

1 个答案:

答案 0 :(得分:10)

任何行式会议(T.stratId = 7 AND TT.stratId = 7)当然必须满足(T.stratId = 7 OR TT.stratId = 7),因此从逻辑上讲,限制性较小的谓词返回的结果较少。

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

和案例

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

enter image description here

或案例

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

enter image description here

Sec的表基数为2399行。在通过联接删除所有行的计划中,SQL Server对IX_Sec_idu进行完全扫描,作为对哈希联接的探测端的输入,但是对该索引的完全扫描仅返回589行

另一个执行计划中出现的行是从另一个索引中提取的,该索引包含这1810个丢失的行。

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

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来查看是否还有其他问题。