ALTER PROCEDURE ReadNews @CategoryID INT, @Culture TINYINT = NULL, @StartDate DATETIME = NULL, @EndDate DATETIME = NULL, @Start BIGINT, -- for paging @Count BIGINT -- for paging AS BEGIN SET NOCOUNT ON; --ItemType for news is 0 ;WITH Paging AS ( SELECT news.ID, news.Title, news.Description, news.Date, news.Url, news.Vote, news.ResourceTitle, news.UserID, ROW_NUMBER() OVER(ORDER BY news.rank DESC) AS RowNumber, TotalCount = COUNT(*) OVER() FROM dbo.News news JOIN ItemCategory itemCat ON itemCat.ItemID = news.ID WHERE itemCat.ItemType = 0 -- news item AND itemCat.CategoryID = @CategoryID AND ( (@StartDate IS NULL OR news.Date >= @StartDate) AND (@EndDate IS NULL OR news.Date <= @EndDate) ) AND news.Culture = @Culture and news.[status] = 1 ) SELECT * FROM Paging WHERE RowNumber >= @Start AND RowNumber <= (@Start + @Count - 1) OPTION (OPTIMIZE FOR (@CategoryID UNKNOWN, @Culture UNKNOWN)) END
Here is the structure of the News and ItemCategory :
CREATE TABLE [dbo].[News]( [ID] [bigint] NOT NULL, [Url] [varchar](300) NULL, [Title] [nvarchar](300) NULL, [Description] [nvarchar](3000) NULL, [Date] [datetime] NULL, [Rank] [smallint] NULL, [Vote] [smallint] NULL, [Culture] [tinyint] NULL, [ResourceTitle] [nvarchar](200) NULL, [Status] [tinyint] NULL CONSTRAINT [PK_News] PRIMARY KEY CLUSTERED ( [ID] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] CREATE TABLE [ItemCategory]( [ID] [bigint] IDENTITY(1,1) NOT NULL, [ItemID] [bigint] NOT NULL, [ItemType] [tinyint] NOT NULL, [CategoryID] [int] NOT NULL, CONSTRAINT [PK_ItemCategory] PRIMARY KEY CLUSTERED ( [ID] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY]
This request reads news of a certain category (sports, politics, ...). The @Culture parameter specifies the language of the news, for example, 0 (English), 1 (French), etc. The ItemCategory table associates a news entry with one or more categories. The ItemType column in the ItemCategory table indicates which type of itemID exists. at the moment, we only have ItemType 0, indicating that itemID refers to an entry in the News table.
Currently, I have the following index in the ItemCategory table:
CREATE NONCLUSTERED INDEX [IX_ItemCategory_ItemType_CategoryID__ItemID] ON [ItemCategory] ( [ItemType] ASC, [CategoryID] ASC ) INCLUDE ( [ItemID])
and the following index for the news table (suggested by the query analyzer):
CREATE NONCLUSTERED INDEX [_dta_index_News_8_1734000549__K1_K7_K13_K15] ON [dbo].[News] ( [ID] ASC, [Date] ASC, [Culture] ASC, [Status] ASC )
With these indexes, when I execute a query, the query is completed in less than a second for some parameters, and for other parameters (for example, different @Culture or @CategoryID) it can take up to 2 minutes! I used OPTIMIZE FOR (@CategoryID UNKNOWN, @Culture UNKNOWN) to prevent the sniffing parameter for the @CategoryID and @Culture , but it does not seem to work for some parameters.
The News table currently has about 2,870,000 entries and ItemCategory in the ItemCategory table.
Now I really appreciate any advice on how to optimize this query or its indexes.
update: execution plan:

(In this image, ItemNetwork is what I called ItemCategory. they are the same)