Here is my query (It is a Microsoft Axapta Query):
(@P1 bigint)
SELECT TOP 1 T1.JOURNALNUM,T1.LINENUM,T1.ACCOUNTTYPE,T1.COMPANY,T1.TXT,
T1.AMOUNTCURDEBIT,T1.CURRENCYCODE,T1.EXCHRATE,T1.TAXGROUP,
T1.CASHDISCPERCENT,T1.QTY,T1.BANKNEGINSTRECIPIENTNAME,
-- *Snipped lots of columns in T1* --
T1.MODIFIEDDATETIME,T1.RECVERSION,T1.PARTITION,T1.RECID
FROM LEDGERJOURNALTRANS T1
WHERE (((PARTITION=123123123) AND (DATAAREAID=N'test')) AND (REVRECID=@P1))
Current execution plan :
Actually, there is a appropriate index on table.
Index columns : (PARTITION,DATAAREAID,REVRECID)
I tried index force. This execution plan (index seek+key lookup) is faster than after plan (index scan):
And I tried to :
-
UPDATE STATISTICS
-
Changed it the column order, for example
(REVRECID,PARTITION,DATAAREAID)
Why does MSSQL choose clustered index?
Best Answer
Estimates, a huge amount of columns selected and predicate pushdown
The query's estimates are not accounting for the residual predicate on the scan being of a higher cost than the seek + key lookup to get all these extra columns from the clustered index. This results in the clustered index scan + residual predicate being chosen instead of the index seek.
These estimates on predicate pushdown where improved in SQL server 2016 SP1
Update to improve diagnostics for query execution plans that involve residual predicate pushdown in SQL Server 2016
This adds the
EstimatedRowsRead=""
to the query plan XML, in your case this would be close or matching the residual predicate if the scan is chosen.This should fix your issue
Residual predicate example
Reading 1.2M rows to return 0
Index scan query Estimated total cost
Index seek query Estimated total cost
Which is higher than the index scan estimations due to not accounting for the residual predicate, and that is why the lesser performing plan was chosen.
The main solution
The main solution would be upgrading to at least SP1 to add the:
Update to improve diagnostics for query execution plans that involve residual predicate pushdown in SQL Server 2016
You should patch sooner and more often, since SP2 CU6 is out as of March 19, 2019, this would be a far better choice.
Another side note, SP1 for SQL Server 2016 adds many additional features such as In memory OLTP, Compression, Columnstore indexes, ....
Other workarounds that may or may not be worth mentioning
OPTION(QUERYTRACEON 4138)
(maybe)WITH(INDEX))
HintComparison with SQL Server 2016 SP1
when running a query alike yours, forcing the clustered index to be used on a SQL2016 SP1 version:
The estimated subtreecost is much higher.
Where your estimated subtreecost for the clustered index scan
Is low
With the main difference being
shown when executing the query on the SQL 2016 with SP1 applied.
And when testing with the NC index specified
the EstimatedTotalSubtreeCost for the index seek (not total for entire plan) is also low:
and the total estimated subtree cost for my test query is very close to yours