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

这怎么可能?

最佳答案

任何符合(T.stratId = 7 AND TT.stratId = 7)的行都必定必须符合(T.stratId = 7 OR TT.stratId = 7),因此从逻辑上讲,限制性较小的谓词返回的结果较少是不可能的。
问题是损坏的非聚集索引。
和Case

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

  • sql-server - SQL Server 2016奇怪的行为-OR条件给出0行但AND条件给出一些行-LMLPHP
    或大小写
  • 发出与日期条件匹配的TradeCore中的1173行
  • 加入TradeTransfer并在3 in (T.fundId, TT.fundId) AND 7 in (T.stratId, TT.stratId)上添加一个剩余谓词,将其减少到73(估计为297行)
  • 然后,通过Sec上的联接消除了所有行-尽管事实上我们从上面知道至少有68个行匹配。

  • sql-server - SQL Server 2016奇怪的行为-OR条件给出0行但AND条件给出一些行-LMLPHPSec的表基数是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来查看是否还有其他问题。

    10-08 19:56