0

I have two tables, Entity and EntityDetails.

Entity table has columns EntityId, Col1, Col2, Col3, LastUpdated (smalldatetime).

EntityDetails table has columns EntityId, Details1, Details2.

Where Col3 in Entity table and Details1 and Details2 in EntiryDetails table are full text indexed.

I also have a NONCLUSTERED INDEX on Entity table.

CREATE NONCLUSTERED INDEX [IX_LastUpdated] ON [dbo].[Entity]
(
    [LastUpdated] DESC,
    [Col2] ASC
)
INCLUDE(EntityId, Col1, Col3) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO

Now, when I perform full text search on full text indexed columns using CONTAINS(EntityDetails.[Details1],EntityDetails.[Details2],EntityDetails.[Details3,@Keywords]), everything works fine if the results are served as it is. But when I add Order By LastUpdated to the query I see 'Worktable' with a lot of Scans and thousands of logical reads in query statistics and Index Spool (Eager).

I thought its probably because the results returned by the fulltext search are in default order of the key column.

So I added a NONCLUSTERED INDEX on EntityDetails.

CREATE UNIQUE NONCLUSTERED INDEX [IX_EntityDetails] ON [dbo].[EntityDetails]
(
    [EntityId] DESC
)
INCLUDE([Details1],[Details2],[Details3]) 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) ON [PRIMARY]
GO

And tried ORDER BY EntityId DESC, but still same.

Can anyone please help?

Update

Here's the query plan Query Plan Full text Search Enger Spool

URL to Query Plan

https://www.brentozar.com/pastetheplan/?id=HyF0nB_9U

Full Text Indexes

CREATE FULLTEXT INDEX ON EntityDetails(Details1,Details2,Details3) KEY INDEX PK_EntityDetails;

CREATE FULLTEXT INDEX ON Entity(Col3) KEY INDEX PK_Entity;

NONCLUSTERED Index on Entity Table

CREATE NONCLUSTERED INDEX [IX_EntityLastUpdated] ON [dbo].[Entity]
(
    [LastUpdated] DESC,
    [Col4] ASC,
    [Col5] ASC,
    [Col6] ASC
)
INCLUDE([EntityId],[Col1],[Col2],[Col3]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO

Update 2

Here's the updated plan with INDEX(PK_Entity) and ORDER BY EntityId DESC

https://www.brentozar.com/pastetheplan/?id=SknlXxj9I

and with INDEX(PK_Entity) and ORDER BY LastUpdated DESC

https://www.brentozar.com/pastetheplan/?id=HkjV7gs98

Update 3

After trying the Answer:

When using

DECLARE @Keywords varchar(255) = 'keyword',
@Col4 tinyint = 162,
@Col3 varchar(255) = 'value';

;WITH Entity_CTE (EntityId, Col1, Col2, Col3, Col4,LastUpdated, Details2, Details1)  
AS  
(  
    SELECT 
        Entity.[EntityId], 
        Entity.[Col1],
        Entity.[Col2], 
        Entity.[Col3], 
        Entity.[Col4],
        Entity.[LastUpdated],
        EntityDetails.[Details2],
        EntityDetails.[Details1]
    FROM [Entity] WITH (NOLOCK) 
        INNER JOIN [EntityDetails] WITH (NOLOCK) on Entity.[EntityId] = EntityDetails.[EntityId] 
    WHERE 
        CONTAINS((EntityDetails.[Details1],EntityDetails.[Details2]), @Keywords) 
        AND 
        Entity.[Col4]=@Col4 
        AND CONTAINS((Entity.[Col3]), @Col3)  
)  
SELECT EntityId, Col1, Col2, Col3, Col4, Details2, Details1  
FROM Entity_CTE
ORDER BY [LastUpdated] DESC OFFSET 0 ROWS FETCH NEXT 20 ROWS ONLY;

I have below IO statistics:

Table 'EntityDetails'. Scan count 0, logical reads 4299, physical reads 0, page server reads 0, read-ahead reads 22, page server read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob page server reads 0, lob read-ahead reads 0, lob page server read-ahead reads 0.
Table 'Entity'. Scan count 1, logical reads 22, physical reads 0, page server reads 0, read-ahead reads 0, page server read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob page server reads 0, lob read-ahead reads 0, lob page server read-ahead reads 0.

When using above query inside a stored procedure:

CREATE PROCEDURE usp_TEMP
@Keywords varchar(255),
@Col4 tinyint = 162,
@Col3 varchar(255)

AS

;WITH Entity_CTE (EntityId, Col1, Col2, Col3, Col4,LastUpdated, Details2, Details1)  
AS  
(  
    SELECT 
        Entity.[EntityId], 
        Entity.[Col1],
        Entity.[Col2], 
        Entity.[Col3], 
        Entity.[Col4],
        Entity.[LastUpdated],
        EntityDetails.[Details2],
        EntityDetails.[Details1]
    FROM [Entity] WITH (NOLOCK) 
        INNER JOIN [EntityDetails] WITH (NOLOCK) on Entity.[EntityId] = EntityDetails.[EntityId] 
    WHERE 
        CONTAINS((EntityDetails.[Details1],EntityDetails.[Details2]), @Keywords) 
        AND 
        Entity.[Col4]=@Col4 
        AND CONTAINS((Entity.[Col3]), @Col3)  
)  
SELECT EntityId, Col1, Col2, Col3, Col4, Details2, Details1  
FROM Entity_CTE
ORDER BY [LastUpdated] DESC OFFSET 0 ROWS FETCH NEXT 20 ROWS ONLY;

I see below IO statistics:

Table 'EntityDetails'. Scan count 0, logical reads 180, physical reads 0, page server reads 0, read-ahead reads 0, page server read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob page server reads 0, lob read-ahead reads 0, lob page server read-ahead reads 0.
Table 'Worktable'. Scan count 1401, logical reads 65571, physical reads 0, page server reads 0, read-ahead reads 0, page server read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob page server reads 0, lob read-ahead reads 0, lob page server read-ahead reads 0.
Table 'Entity'. Scan count 1, logical reads 22, physical reads 0, page server reads 0, read-ahead reads 0, page server read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob page server reads 0, lob read-ahead reads 0, lob page server read-ahead reads 0.

Update 4

It seems that using OPTION(QUERYTRACEON 4136) fixes the issue, but I am not sure about possible implications, does it has any negative impact?

Naveed Ahmed
  • 111
  • 6
  • Hi, Naveed Ahmed. Please, we'll be able to better help you if your performance question contained the execution plan and query being executed, like this. – Ronaldo May 12 '20 at 11:35
  • Thank you so much Ronaldo for your reply, I have added the query plan to the question, can you please take a look. – Naveed Ahmed May 12 '20 at 16:10
  • Is there a performance problem or you just want to avoid a worktable in your exec plan? – LowlyDBA - John M May 12 '20 at 16:35
  • The query performance is poor (runs slow) when I apply ORDER BY LastUpdated DESC. And I want the results to be sorted by last modified date. If I make ORDER BY EntityId the results are returned very quickly, I want the same performance but with LastUpdated DESC.Is it possible? I think the performance problem is due to the worktable/ Index Spool(Eager) – Naveed Ahmed May 12 '20 at 16:58
  • Could you also run the query without the ORDER BY using the same parameters and add that query plan so that we can compare them? – Ronaldo May 13 '20 at 17:35
  • Here you go https://www.brentozar.com/pastetheplan/?id=HyelYg99I – Naveed Ahmed May 13 '20 at 22:48
  • It's possible that using the index IX_EntityLastUpdated is the problem. The best use of an index is when a predicate references the index key in a JOIN condition or WHERE clause, but your query has no predicate on the column LastUpdated that is the key of that index. But by adding the ORDER BY Entity.[LastUpdated] DESC I think you made SQL Server think that index would be the best choice for the query. Let's try to force it not to use that index and see if that makes sence. – Ronaldo May 14 '20 at 14:44
  • To do that try runing the query with bad performance substituting the FROM you have for this: FROM [Entity] WITH (NOLOCK, INDEX(PK_Entity)) INNER JOIN [EntityDetails] WITH (NOLOCK) on Entity.[EntityId] = EntityDetails.[EntityId] and see if it generates the same execution plan of the query with good performance. – Ronaldo May 14 '20 at 14:46
  • I have updated the post with the updated plans, can you please take a look? – Naveed Ahmed May 14 '20 at 16:37
  • definitely not the result I was expecting. But since the query hint spoiled the plan of the ORDER BY EntityId DESC when it should not change, I think it wasn't the best way to avoid the use of the index IX_EntityLastUpdated. Was that index created for this specifc query we're trying to tune? if yes, could you disable it and run your original query? (if it's production environment and you're not sure if it's used by other queries, don't disable the index). – Ronaldo May 14 '20 at 20:51
  • I also rewrote the query and I'm gonna have to post as an answer because it doesn't fit on this comment space, but it's a guess and I'm out of ideas for now. @LowlyDBA, any insight based on the new inputs of the question? – Ronaldo May 14 '20 at 21:00
  • Yes I added this index for this specific query, because if you look at this plan https://www.brentozar.com/pastetheplan/?id=BkU6bEj5I you will notice the sort operation which I wanted to avoid and there was a clustered index scan which was returning millions of rows. – Naveed Ahmed May 14 '20 at 21:04

0 Answers0