'How to best use multicolumn index with value ranges in SQL Server?

I'm running SQL Server 2016 Enterprise edition.

I have a table with 24 columns and 10 indexes. Those indexes are vendor defined so I cannot change them. I have a hard time to understand how to get best performance as whatever I do SQL Server chooses in my opinion a poor execution plan.

The following query :

SELECT event_id
FROM Events e WITH(NOLOCK, index=[Event_By_PU_And_TimeStamp])
WHERE e.timestamp > '2022-05-12 15:00' 
  AND e.PU_Id BETWEEN 103 AND 186

results in this index seek:

enter image description here

The specified index is the clustered index and it has two columns PU_ID and Timestamp. Even though the SEEK PREDICATE lists both PU_ID and Timestamp as the used columns the "Number of rows read" is too high in my opinion. Without the index hint SQL chooses a different index for the seek with double rows-read number.

Unfortunately the order of the columns in the index is PU_ID, Timestamp, while Timestamp is the much more selective column here.

However if I change the PU_ID condition to list every possible number between the margins

PU_ID IN (103,104,105,...186)

then the "rows read are exactly the number of returned rows" and the statistics output confirms a better performance (validated with profiler trace).

Between-condition:

(632 rows affected)
Table 'Events'. Scan count 7, logical reads 139002, physical reads 0, read-ahead reads 1, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

IN-condition with every number written out:

(632 rows affected)
Table 'Events'. Scan count 84, logical reads 459, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

Edit: the IndexSeek for the IN-query enter image description here

What is the best way to make SQL Server choose the better plan?

Do I really need to write out all possible PU_IDs in every query?

The used index is just a simple two column index, it's just the clustered index as well:

CREATE UNIQUE CLUSTERED INDEX [Event_By_PU_And_TimeStamp] ON [dbo].[Events]
(
  [PU_Id] ASC,
  [TimeStamp] ASC
)


Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source