Chapter 09 · Index Engineering: Rowstore, Filtered, Included, Computed, and Specialized Indexes

Filtered Indexes, Computed-Column Indexes, Unique Indexes, and Constraint Interactions

Use filtered, computed-column, and unique indexes when predicates and invariants match exactly, with SET-option, determinism, parameterization, NULL, and constraint semantics made explicit.

Advanced125–165 minutesFiltered/computed/unique index labSQL Server 2025 CU7 · 17.0.4065.4Compatibility 170 · Developer/ExpressLast reviewed: August 2026

Learning outcomes

Some ServiceHub access paths are valuable only for a narrow subset of rows or for a normalized expression that is not stored directly. SQL Server provides filtered indexes and indexes on computed columns for these cases, while unique indexes can enforce an invariant and provide an access path. These features are powerful precisely because their matching rules are strict: predicate implication, parameterization, data-type conversion, determinism, precision, SET options, and NULL semantics all matter.

01

Design filtered indexes around stable, selective subsets.

02

Explain why parameterized predicates may not prove a filtered-index condition.

03

Create searchable deterministic computed expressions safely.

04

Use unique indexes for integrity while understanding NULL behavior.

05

Verify required SET options and constraint relationships.

1. Filter only the rows the workload repeatedly needs

A filtered index is a nonclustered rowstore index with a WHERE predicate. It can have smaller storage and more focused statistics than a full-table index. In ServiceHub, dispatchers repeatedly search only open/escalated rows; an index for the active subset can therefore be more efficient than indexing closed history too.

sql · build a filtered active-work index
USE ServiceHubLab;GOIF SCHEMA_ID(N'lab09') IS NULL EXEC(N'CREATE SCHEMA lab09 AUTHORIZATION dbo;');DROP TABLE IF EXISTS lab09.FilterProbe;CREATE TABLE lab09.FilterProbe(  work_order_id bigint IDENTITY PRIMARY KEY,  external_ref varchar(40) NULL,  status varchar(20) NOT NULL,  opened_at datetime2(3) NOT NULL,  customer_id int NOT NULL,  contact_phone varchar(30) NULL);;WITH n AS( SELECT TOP (20000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS n FROM sys.all_objects a CROSS JOIN sys.all_objects b)INSERT lab09.FilterProbe(external_ref,status,opened_at,customer_id,contact_phone)SELECT CASE WHEN n%17=0 THEN NULL ELSE CONCAT('WO-',n) END,       CASE WHEN n%20=0 THEN 'ESCALATED' WHEN n%4=0 THEN 'CLOSED' ELSE 'OPEN' END,       DATEADD(minute,n,'2026-01-01'),1+n%1000,       CASE WHEN n%9=0 THEN '(555) 010-' + RIGHT('0000'+CAST(n%10000 AS varchar(4)),4) ENDFROM n;GOCREATE INDEX IX_FilterProbe_ActiveON lab09.FilterProbe(status,opened_at)INCLUDE(customer_id)WHERE status IN ('OPEN','ESCALATED');GO

The query optimizer can use a filtered index when it can prove that a query's qualifying rows are contained in the filter. That proof can become difficult with parameters whose runtime values are not known or whose possible values include rows outside the filter.

2. Parameterization and predicate equivalence are part of the contract

A literal query for status='OPEN' clearly implies the active filter. A reusable parameter @status might be 'CLOSED', so a cached generic plan cannot blindly assume the filtered index contains every possible result. Options include separate statement shapes, recompilation where justified, or a broader index. Do not force a filtered index that is logically incomplete for a possible parameter value.

sql · compare literal and parameterized shapes
-- Literal: optimizer can reason about this exact value.SELECT work_order_id,customer_id,opened_atFROM lab09.FilterProbeWHERE status='OPEN'  AND opened_at >= '2026-01-05';GODECLARE @status varchar(20)='OPEN';SELECT work_order_id,customer_id,opened_atFROM lab09.FilterProbeWHERE status=@status  AND opened_at >= '2026-01-05';GO-- If business semantics truly justify per-execution compilation:DECLARE @status2 varchar(20)='OPEN';SELECT work_order_id,customer_id,opened_atFROM lab09.FilterProbeWHERE status=@status2  AND opened_at >= '2026-01-05'OPTION (RECOMPILE);GO

Inspect actual plans instead of assuming one plan. Recompile trades compilation CPU for value-specific optimization; it is not a universal filtered-index fix.

3. Computed columns can make transformations searchable

A query that repeatedly applies a deterministic transformation to a column can benefit from a computed column whose result is indexable. Indexing a computed column requires ownership, determinism/precision, data-type, and SET-option conditions. PERSISTED stores the computed value in the table and can expand the set of indexable deterministic expressions, but it also adds storage/write work.

sql · create and index a deterministic normalization
ALTER TABLE lab09.FilterProbeADD phone_digits AS  REPLACE(REPLACE(REPLACE(REPLACE(contact_phone,'(',''),')',''),' ',''),'-','') PERSISTED;GOCREATE INDEX IX_FilterProbe_PhoneDigitsON lab09.FilterProbe(phone_digits)WHERE phone_digits IS NOT NULL;GOSELECT work_order_id,contact_phone,phone_digitsFROM lab09.FilterProbeWHERE phone_digits='5550101224';GOSELECT SESSIONPROPERTY('ANSI_NULLS') AS ANSI_NULLS,       SESSIONPROPERTY('ANSI_PADDING') AS ANSI_PADDING,       SESSIONPROPERTY('ANSI_WARNINGS') AS ANSI_WARNINGS,       SESSIONPROPERTY('ARITHABORT') AS ARITHABORT,       SESSIONPROPERTY('CONCAT_NULL_YIELDS_NULL') AS CONCAT_NULL_YIELDS_NULL,       SESSIONPROPERTY('QUOTED_IDENTIFIER') AS QUOTED_IDENTIFIER;GO
Failure mode

Indexed computed columns and filtered indexes depend on required SET options. A deployment/session that violates required options can fail DML or make the optimizer ignore the index. The fix is not to disable correctness options; standardize connection/session settings and deployment scripts, then verify them.

4. Unique indexes enforce a rule, including SQL Server NULL semantics

A unique index can enforce a candidate-key rule without being the table's primary key. In SQL Server a single-column unique index treats two NULLs as equal for uniqueness enforcement, so at most one NULL can exist. If the business rule is “many rows may have no external reference, but non-NULL references must be unique,” a filtered unique index is a better expression of the invariant.

sql · enforce uniqueness only for populated external references
CREATE UNIQUE INDEX UX_FilterProbe_ExternalRefON lab09.FilterProbe(external_ref)WHERE external_ref IS NOT NULL;GO-- This is allowed: many rows can remain NULL because they are outside the filter.INSERT lab09.FilterProbe(external_ref,status,opened_at,customer_id)VALUES (NULL,'OPEN',SYSUTCDATETIME(),9999),       (NULL,'OPEN',SYSUTCDATETIME(),9998);GO-- This would fail if an existing row already has WO-100:-- INSERT lab09.FilterProbe(external_ref,status,opened_at,customer_id)-- VALUES ('WO-100','OPEN',SYSUTCDATETIME(),9997);GO

A unique constraint typically creates a unique index, but the concepts are not identical. Constraints express relational integrity in schema semantics; indexes are physical access/enforcement structures. A foreign key, for example, does not automatically create an index on the referencing columns. Chapter 04's constraint-trust rules still apply.

5. Production judgment and cleanup

Use filtered indexes only when the subset is stable and important enough to justify a separate structure. Test real parameterization patterns. For computed columns, review determinism, precision, collation and implicit conversion behavior; index only expressions that reduce repeated runtime work or enable a genuinely useful access path. For uniqueness, encode the exact NULL/business semantics rather than relying on application checks.

sql · cleanup lesson 3
DROP TABLE IF EXISTS lab09.FilterProbe;GO

Check your understanding

  1. Why might a parameterized status query not use a filtered active-row index?
  2. What does PERSISTED do for a computed column?
  3. Can a single-column SQL Server unique index contain multiple NULLs?
  4. Does a foreign key automatically create an index on referencing columns?
  5. What is the safer rule for SET options?
Review the answers

Because the reusable plan may need to be correct for parameter values outside the filter.

Stores the computed value and maintains it on writes; it can also make some deterministic imprecise expressions indexable.

No; without a filter, only one NULL is allowed because NULLs compare as equal for unique index enforcement.

No.

Standardize and verify required options rather than changing them ad hoc to force an index.

Authoritative references

Keep knowledge open

Help the academy stay free and grow.

If these tutorials save you time, a small donation supports new lessons, technical review, diagrams, examples, and long-term maintenance.

ETHEthereum / ERC-20 only
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0

Send only Ethereum or ERC-20 compatible assets to this address.