A materialized view (indexed view) stores the pre-computed result set of a SELECT query on disk, letting SQL Server serve reads instantly without re-executing the query.
Materialized views persist aggregated or join-heavy query results. SQL Server answers repeated analytics queries in milliseconds because data is already calculated and indexed.
Use CREATE VIEW ... WITH SCHEMABINDING
to lock schema, then add a unique clustered index.In Azure SQL Synapse or SQL Server 2022 you can use the native CREATE MATERIALIZED VIEW
command.
SCHEMABINDING
prevents underlying table changes, CLUSTERED INDEX
materializes storage, and WITH (NOEXPAND)
tells the optimizer to read the index directly.
SQL Server auto-maintains indexed views during DML on base tables.For bulk loads, enable SET INDEXES ON
or drop and recreate the index after loading.
CREATE VIEW dbo.v_daily_customer_sales
WITH SCHEMABINDING AS
SELECT o.customer_id,
CAST(o.order_date AS date) AS sales_day,
SUM(o.total_amount) AS daily_sales,
COUNT_BIG(*) AS order_count
FROM dbo.Orders AS o
GROUP BY o.customer_id, CAST(o.order_date AS date);
GO
-- Materialize it
CREATE UNIQUE CLUSTERED INDEX IX_v_daily_customer_sales
ON dbo.v_daily_customer_sales(customer_id, sales_day);
NOEXPAND
in queries or set the database option to treat all indexed views that way.Non-deterministic functions: GETDATE() blocks materialization.Replace with persisted column or parameter.
Missing unique clustered index: Without it, the view stays virtual. Always create the index after the view.
Query it like a table: SELECT * FROM dbo.v_daily_customer_sales WITH (NOEXPAND);
.
Yes. DML on base tables triggers incremental updates to the indexed view. No manual REFRESH command is needed.
You can, but the view reverts to virtual and performance benefits disappear. Drop only if the view is no longer queried.
Add the WITH (NOEXPAND)
hint, or set the database option CREATE VIEW OR ALTER DATABASE SET MATERIALIZED_VIEW_HINTS = ON
in newer versions.