Filtered Indexes first appeared in SQL Server 2008. As the
name implies, they can create a subset of a full index. They improve query
performance, reduce index maintenance costs, and reduce index storage. Also, an
indirect benefit is being able to enforce unique constraints for primary or
active records.
Filtered Indexes are a great addition to SQL Server. For
data warehouse dimension tables, you can filter on the active flag and greatly
reduce the index size and get a much better query plan. As the warehouse grows,
the active records can quickly become less than 1% of the table. In the
mortgage industry, you will have a primary title holder and the co-signers. A
third example would be health insurance. There is a card holder and dependents
on policies with a family subscription. Since most processes go through the
card holder, you want to make this as fast as possible.
Let’s use a typical family insurance policy. Below is a
sample of 3 policies with one or more members per policy.
PolicyID
|
MemberID
|
IsPrimary
|
100
|
1
|
1
|
100
|
2
|
0
|
100
|
3
|
0
|
200
|
4
|
1
|
200
|
5
|
0
|
300
|
6
|
1
|
The standard index can be created with:
CREATE NONCLUSTERED INDEX IX_PolicyMember_PolicyID_IsPrimary
ON [dbo].[PolicyMember] (PolicyID, IsPrimary)
This will work; but, will be less efficient than a filtered
index if you have of millions of policies and tens of millions of members.
To create a Filtered Index, we only have to add a WHERE
clause.
CREATE NONCLUSTERED INDEX IX_PolicyMember_PolicyID_IsPrimary
ON [dbo].[PolicyMember] (PolicyID, IsPrimary)
where IsPrimary = 1;
This will get us the benefits mentioned earlier; but, it
will not stop us from adding a second Primary person to the policy.
INSERT INTO [dbo].[PolicyMember]
([PolicyID]
,[MemberID]
,[IsPrimary])
VALUES
(100,
7, 1);
Now we have two records with a Primary indicator of 1.
PolicyID
|
MemberID
|
IsPrimary
|
100
|
1
|
1
|
100
|
2
|
0
|
100
|
3
|
0
|
200
|
4
|
1
|
200
|
5
|
0
|
300
|
6
|
1
|
100
|
7
|
1
|
This will cause problems. We would have to ensure the
application and any manual updates are performed correctly.
With one more little change we can get the increased
performance with a constraint of only one primary record per policy by adding a
unique filtered index. First let’s delete the last row added.
Delete PolicyMember
where MemberID = 7;
Create a unique filtered index:
CREATE UNIQUE NONCLUSTERED INDEX IX_PolicyMember_PolicyID_IsPrimary
ON [dbo].[PolicyMember] (PolicyID, IsPrimary)
where IsPrimary = 1;
Now if we try to add member 7 as primary, we get an error.
INSERT INTO [dbo].[PolicyMember]
([PolicyID]
,[MemberID]
,[IsPrimary])
VALUES
(100,
7, 1);
Cannot
insert duplicate key row in object 'dbo.PolicyMember' with unique index
'IX_PolicyMember_PolicyID_IsPrimary'. The duplicate key value is (100, 1).
The
statement has been terminated.
We are always looking for ways to increase performance. With
this recently added feature, we can get a lot of benefit without application
changes. Take a look at some of your performance bottlenecks and see if they
are good candidates for the Filtered Index.