Code: Fixing a Filtered Index Which is Causing Data Modifications to Fail
This question came up in a webcast recently: if a filtered index is causing data modifications to fail, does disabling the filtered index fix the …
Read Moreon • 7 min read
SQL Server has tools that suggest indexes– and they’ll even auto-create the indexes for you.
I’m not a huge fan of these tools because they’ve got some notable flaws: they lead to creating more indexes than you need, and they aren’t super smart about the indexes they recommend.
I’m using the free BabbyNames sample database (small version). All the code in this example is also available in a gist for easy access.
I run two queries, which each scan the clustered index of agg.FirstNameByYear:
USE BabbyNames;
GO
SET NOCOUNT ON;
GO
SELECT
ref.FirstName,
agg.NameCount
FROM agg.FirstNameByYear as agg
JOIN ref.FirstName as ref on
agg.FirstNameId=ref.FirstNameId
WHERE
Gender='F'
and ref.FirstName = 'Calliope';
GO 10
SELECT TOP 100
ref.FirstName,
agg.NameCount
FROM agg.FirstNameByYear as agg
JOIN ref.FirstName as ref on
agg.FirstNameId=ref.FirstNameId
WHERE
Gender = 'M'
ORDER BY NameCount DESC;
GO 20
SQL Server notices the clustered index scans when it optimizes these queries, and it quickly figures out that a nonclustered index would make these queries more efficient. It generates missing index requests.
You can see missing index requests in the execution plans as green hints (as seen in yesterday’s post). You can also query them from SQL Server’s Dynamic Management Views with a query like this:
SELECT
d.statement as table_name,
d.equality_columns,
d.inequality_columns,
d.included_columns,
s.avg_total_user_cost as avg_est_plan_cost,
s.avg_user_impact as avg_est_cost_reduction,
s.user_scans + s.user_seeks as times_requested
FROM sys.dm_db_missing_index_groups AS g
JOIN sys.dm_db_missing_index_group_stats as s on
g.index_group_handle=s.group_handle
JOIN sys.dm_db_missing_index_details as d on
g.index_handle=d.index_handle
JOIN sys.databases as db on
d.database_id=db.database_id
WHERE db.database_id=DB_ID();
GO
* This simple query doesn’t sort or prioritize the requests, it just returns the raw info
Here’s what the missing index requests for agg.FirstNameByYear look like in this case:

The missing index feature has a few limitations. The limitations are documented, but the problem is that most people don’t know about the documentation. The feature doesn’t suggest key column order or fine tune your indexes.
But I think the biggest limitation is that the missing index feature doesn’t provide a way to learn about the queries making the request – and that link would be extremely helpful so that you can fine tune your indexes!
There are a couple of problems with the requests for these relatively simple queries:
We have confusing advice. If we don’t know better, we’ll end up over-indexing – and still not creating the best index for one of the queries.
I don’t love running the DTA directly against SQL Servers that you care about: if it fails during its run, it leaves a bunch of temporary objects behind. I know that this happens a lot, because I’ve found those temporary objects in SQL Servers scattered around the world. It’s like space trash: it doesn’t pose an immediate threat, but it’s sloppy.
But, for science, I ran the DTA against this database, using the execution plan cache after I ran these queries.
The DTA suggested that I create four indexes:

We’ve got problems here too, it just looks fancier.
Overindexing isn’t great: it wastes storage, wastes memory, prolongs index maintenance and corruption checking, and makes restores take longer.
This isn’t harmless. It’s like throwing junk in your closet. At first it’s no big deal. Eventually, you can barely open the door without things falling on your head.

The query that runs 10 times would do very well with either:
The query that runs 20 times would do very well with:
We have a small conundrum here, because key order really matters. There’s no “perfect” way to combine these indexes, because only one column can be second in the list of keys. And ‘Gender’ is not very selective as a leading key column.
I see this as a prioritization problem. What are the performance requirements for each query?
Once we know that, the next problem is, how long are the queries actually taking? It’s possible that I don’t need to do anything and that the performance of my queries is totally acceptable.
Remember: just because you see an index request, that doesn’t prove you have slow queries! All it means is that the optimizer guessed something could be faster when it was guessing about how to run a query, before the query even executed.
If the “TOP 100” query is more important and I create only the following index, it’s great for the ‘TOP 100" query. It’s good enough for the other query to result in CPU time of 45 milliseconds and elapsed time of 18 milliseconds.
CREATE INDEX ix_iamapersonnotamachine on agg.FirstNameByYear
(Gender, NameCount) INCLUDE (FirstNameId);
GO
* Sorry about the terrible index name
* I didn’t specify NameCount as DESC. It doesn’t make a difference in this case: SQL Server can do a backward scan. Although that zone of the plan will be serial, it’s feeding into a TOP operator anyway, which requires a serial zone. And it’s able to stop after it reads 100 rows.
If we frequently use predicates on the FirstName column on ref.FirstName, it makes sense to index it, even though it’s a tiny table. So I’d do this:
CREATE INDEX ix_FirstName_FirstName_INCLUDES on ref.FirstName
(FirstName) INCLUDE (FirstNameId);
GO
The ‘Missing Indexes’ feature is like a two year old: it asks for what it wants in the moment. It often asks for too much, or not the right thing. That’s understandable: these index requests have to be generated super fast during query optimization. SQL Server doesn’t have time to sit around and ponder. It’s up to you to figure out why it’s asking for something and whether or not it should get it – and if you want to do that well, you’re going to have to track down the queries that are making the requests.
That means doing some legwork and identifying the query execution plans that are the most important to index. You can do that using the execution plan cache, Query Store, or a custom monitoring tool.
As for the Database Tuning Advisor, it doesn’t have the excuse of having to work super fast during query compilation. It’s still prone to over-indexing and littering in your database if it hits a hiccup. I’ve also known it to recommend some really wacky clustered indexes that people applied because they thought the DTA must follow best practices. I just can’t find a way to be a fan of the DTA.
Copyright (c) 2025, Catalyze SQL, LLC; all rights reserved. Opinions expressed on this site are solely those of Kendra Little of Catalyze SQL, LLC. Content policy: Short excerpts of blog posts (3 sentences) may be republished, but longer excerpts and artwork cannot be shared without explicit permission.