I have a query of the following form:
IF EXISTS (
SELECT 1
FROM (
SELECT RowID, OETID
FROM @InMemoryTableTypeTable i
UNION
SELECT RowID, OETID
FROM @InMemoryTableTypeTable d
) AS t
WHERE NOT EXISTS (
SELECT 1
FROM dbo.MyTable m WITH(FORCESEEK, ROWLOCK, UPDLOCK)
WHERE (m.OETID = t.RowID)
AND (m.SRID = t.OETID)
AND (m.WTID = @WTID)
AND (m.Status <> 1)
AND (m.SRID > 0)
)
)
...
The definition of dbo.MyTable is:
CREATE TABLE [dbo].[MyTable](
[ID] [bigint] IDENTITY(1,1) NOT NULL,
[RowGUID] [uniqueidentifier] ROWGUIDCOL NOT NULL,
[WTID] [bigint] NOT NULL,
[OETID] [int] NOT NULL,
[SRID] [bigint] NOT NULL,
[Status] [tinyint] NOT NULL,
CONSTRAINT [PK_MyTable] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
CREATE UNIQUE NONCLUSTERED INDEX [IDX] ON [dbo].[MyTable]
(
[WTID] ASC,
[OETID] ASC,
[SRID] ASC
)
INCLUDE([Status])
WHERE ([SRID]>(0))
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
GO
ALTER TABLE [dbo].[MyTable] ADD CONSTRAINT [DF_MyTable_RowGUID] DEFAULT (NEWID()) FOR [RowGUID]
GO
The definition of @InMemoryTableTypeTable is
CREATE TYPE [dbo].[TableType] AS TABLE(
[ID] [bigint] NOT NULL,
[RowID] [int] NOT NULL,
[OETID] [int] NOT NULL,
PRIMARY KEY NONCLUSTERED
(
[ID] ASC
)
)
WITH ( MEMORY_OPTIMIZED = ON )
GO
The table MyTable contains ~500k rows and has a unique filtered index that has:
WTID,OETIDandSRIDas keys in that order- a filter where
SRID> 0 Statusas an included column
This means that the EXISTS statement is SARGable.
However, depending on how many records are in @InMemoryTableTypeTable and what mood SQL Server seems to be in, sometimes the index seek will only seek on WTID and push the rest of the predication up into the Left Anti Semi Join. If this happens and memory on the SQL Server itself is under pressure, the query can sit there for 20 minutes or so. For some values of @WTID there could be 1 row or there could be 200k that have just been inserted earlier in the same session.
Here is the good plan: https://www.brentozar.com/pastetheplan/?id=H1-V_Jz7R
Here is the bad plan: https://www.brentozar.com/pastetheplan/?id=SJD-QZGQA
Is there a way to force SQL Server to apply the predication to all 3 columns in the Index Seek every time?
I have tried breaking this out of the IF and using the OPTIMIZE FOR UNKNOWN and OPTIMIZE FOR (@WTID UNKNOWN) hints to no avail.
The seek is more for concurrency: the reads and writes within that table for each session will be segregated by WTID. However, removing those table hints makes no difference, it always scans t and seeks into m, it's the position of the OETID and SRID predication that appears to make the difference.
This post Actual and Estimated rows differ greatly led me to the ASSUME_MIN_SELECTIVITY_FOR_FILTER_ESTIMATES hint which produces the plan I want (most of the time) along with RECOMPILE. Combining this with FORCE_LEGACY_CARDINALITY_ESTIMATION reverts to the "wrong" plan.
