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

Ami*_*mar 6 t-sql sql-server

我有以下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
Run Code Online (Sandbox Code Playgroud)

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

这怎么可能?

Mar*_*ith 10

Any row meeting (T.stratId = 7 AND TT.stratId = 7) must certainly meet (T.stratId = 7 OR TT.stratId = 7) so it is not logically possible that the less restrictive predicate returns less results.

The issue is a corrupt non clustered index.

And Case

  • 154 rows in TradeCore matching the date condition and stratId = 7 are emitted.
  • Join on TradeTransfer with the stratId and fundId conditions applied ouputs 68 rows (estimated 34 rows)
  • These all successfully join onto a row in Sec (using index IX_Sec_secId_sectype_Ccy_valpoint) and 68 rows are returned as the final result.

在此处输入图片说明

Or case

  • 1173 rows in TradeCore matching the date condition are emitted
  • Join on TradeTransfer with a residual predicate on 3 in (T.fundId, TT.fundId) AND 7 in (T.stratId, TT.stratId) brings this down to 73 (estimated 297 rows)
  • Then all rows are eliminated by the join on Sec - despite the fact that we know from above that at least 68 of them have a match.

在此处输入图片说明

The table cardinality of Sec is 2399 rows. In the plan where all rows are removed by the join SQL Server does a full scan on IX_Sec_idu as input to the probe side of the hash join but the full scan on that index only returns 589 rows.

The rows that appear in the other execution plan are pulled from a different index that contains these 1,810 missing rows.

You have confirmed in the comments that the following return differing results

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
Run Code Online (Sandbox Code Playgroud)

This should never be the case that rowcounts from different indexes on the same table don't match (except if an index is filtered and that does not apply here).

Reason for different indexes

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

固定

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

您还应该运行dbcc checkdb以查看是否还有其他问题。