DP-900Chapter 22 of 101Objective 2.3

Indexes, Views, and Stored Procedures

This chapter covers indexes, views, and stored procedures — three essential database objects that optimize query performance, simplify data access, and encapsulate business logic in relational databases. For the DP-900 exam, approximately 15-20% of questions in Domain 2 (Relational Data) relate to these objects, making them a critical area to master. You will learn exactly what each object is, how it works internally, when to use it, and — most importantly — how Microsoft expects you to distinguish between them on the exam.

25 min read
Intermediate
Updated May 31, 2026

Database Index: Library Card Catalog

Imagine a public library with 100,000 books. Without a card catalog, finding a book titled "Azure Data Fundamentals" means you must walk every aisle, inspecting each book spine until you find it — that's a full table scan. The library staff creates a card catalog (the index) where each card lists a book title and its exact shelf location (row, section, shelf number). When you search for a book, you go to the catalog, find the card alphabetically, note the location, and walk directly to that shelf. This is a clustered index if the books are physically arranged on the shelves in the same order as the catalog — the catalog mirrors the physical order. If the catalog is separately alphabetized but the books remain in random order, that's a nonclustered index: the catalog gives you a pointer (the shelf location) but the books aren't sorted that way. Maintaining the catalog takes effort — every time a new book arrives, a card must be inserted in the correct alphabetical position. If you add too many indexes, the librarians spend all their time filing cards instead of helping patrons. Similarly, database indexes speed up reads but slow down writes because the index must be updated on every INSERT, UPDATE, or DELETE.

How It Actually Works

What Are Indexes?

An index is a database structure that improves the speed of data retrieval operations on a table at the cost of additional writes and storage. Without an index, the database engine must perform a full table scan — reading every row — to find matching data. With an index, the engine can perform an index seek, reading only the relevant pages.

Clustered Indexes

A clustered index determines the physical order of data in a table. There can be only one clustered index per table because the data rows themselves can be sorted in only one physical sequence. When you create a primary key on a table, SQL Server by default creates a clustered index on that key column. If no clustered index exists, the table is a heap — rows are stored in no particular order.

Internally, a clustered index is structured as a B-tree (balanced tree). The leaf level of the B-tree contains the actual data pages. The intermediate levels contain index pages that guide the search from the root to the leaf. For example, if you search for an ID of 500, the root page might point to a page covering IDs 1-1000, then a lower page covering 400-600, then the leaf page containing row 500. This structure allows an index seek in O(log n) time versus O(n) for a full scan.

Nonclustered Indexes

A nonclustered index is a separate structure from the data table. It contains a sorted list of index key values and, for each key, a pointer to the actual data row. If the table has a clustered index, the pointer is the clustered index key. If the table is a heap, the pointer is the row's physical location (RID — Row Identifier).

You can create up to 999 nonclustered indexes per table in SQL Server (though practically far fewer). Each nonclustered index requires its own storage and must be maintained on data modifications. The leaf level of a nonclustered index contains the index key columns plus included columns (if any) and the row locator.

Key Decisions: Choosing Index Columns

Clustered index: Choose a column that is unique, ever-increasing (e.g., an identity column), and frequently used in range queries. GUID columns make poor clustered indexes because their random order causes page splits.

Nonclustered index: Choose columns that appear frequently in WHERE clauses, JOIN conditions, and ORDER BY. For covering indexes, include all columns needed by a query to avoid key lookups.

What Are Views?

A view is a virtual table based on the result set of a SELECT query. It does not store data physically (unless it is an indexed view). Views provide security by restricting access to specific columns or rows, simplify complex queries by encapsulating joins and filters, and ensure consistency by presenting a standardized schema.

#### Types of Views in SQL Server

Standard view: A saved SELECT query. Data is always current because it is retrieved from underlying tables at query time.

Indexed view (also called materialized view): A view that has a unique clustered index created on it. The result set is physically stored, like a table, and updated automatically when underlying data changes. Indexed views dramatically improve performance for aggregations and joins that are queried frequently. However, they require specific settings (e.g., SET ANSI_NULLS ON, SET QUOTED_IDENTIFIER ON) and cannot reference other views or contain certain constructs like TOP, DISTINCT, or subqueries.

Partitioned view: Joins horizontally partitioned data from multiple tables into a single view. Used in distributed scenarios.

#### Limitations of Views

You cannot use ORDER BY in a view definition unless you also use TOP or OFFSET...FETCH.

Views cannot have triggers or indexes (except indexed views, which have a clustered index).

Modifications through views are limited — they must update only one underlying table at a time, and the view must include all columns with NOT NULL constraints that lack defaults.

What Are Stored Procedures?

A stored procedure is a precompiled collection of Transact-SQL statements stored under a name and processed as a unit. Stored procedures accept input parameters, return output parameters, and can return result sets. They are stored in the database and executed on the server.

#### Benefits of Stored Procedures

Performance: Execution plans are cached and reused, reducing compilation overhead on subsequent executions.

Security: Permissions can be granted on the stored procedure without granting direct table access, implementing the principle of least privilege.

Code reuse: Business logic is written once and called from multiple applications.

Network traffic: Multiple statements can be sent in a single call, reducing round trips.

#### How Stored Procedures Work Internally

When a stored procedure is first executed, SQL Server parses it, checks for syntactic errors, and creates an execution plan. The plan is stored in the plan cache. On subsequent executions, if an existing plan is found and still valid, it is reused. Parameters are sniffed — the first execution's parameter values are used to optimize the plan. This can lead to parameter sniffing issues if subsequent calls use very different values.

#### Stored Procedures vs. Functions

Stored procedures can have side effects (INSERT, UPDATE, DELETE) and return multiple result sets. Functions must be deterministic and cannot modify data.

Stored procedures can use transaction control (BEGIN TRANSACTION, COMMIT, ROLLBACK). Functions cannot.

Stored procedures can return output parameters; functions return a scalar value or a table.

Stored procedures can call functions; functions cannot call stored procedures.

Indexes, Views, and Stored Procedures in Azure SQL Database

All three objects work identically in Azure SQL Database and SQL Server on-premises. However, there are some Azure-specific considerations:

Index maintenance: In Azure SQL Database, index rebuilds and reorganizations are supported, but you must ensure they don't exceed DTU/vCore limits.

Indexed views: Supported in Azure SQL Database and Azure Synapse Analytics (dedicated SQL pool) but have restrictions.

Stored procedures: The same T-SQL syntax works. Use sp_executesql for dynamic SQL with parameterization to avoid SQL injection.

Common Commands

#### Index Commands

-- Create a clustered index
CREATE CLUSTERED INDEX IX_Orders_OrderDate ON dbo.Orders (OrderDate);

-- Create a nonclustered index
CREATE NONCLUSTERED INDEX IX_Orders_CustomerID ON dbo.Orders (CustomerID) INCLUDE (OrderDate, TotalAmount);

-- Rebuild an index
ALTER INDEX IX_Orders_CustomerID ON dbo.Orders REBUILD;

-- Reorganize an index
ALTER INDEX IX_Orders_CustomerID ON dbo.Orders REORGANIZE;

#### View Commands

-- Create a view
CREATE VIEW Sales.vOrdersSummary AS
SELECT CustomerID, COUNT(*) AS OrderCount, SUM(TotalAmount) AS TotalAmount
FROM Sales.Orders
GROUP BY CustomerID;

-- Create an indexed view
CREATE VIEW Sales.vOrdersSummary WITH SCHEMABINDING AS
SELECT CustomerID, COUNT_BIG(*) AS OrderCount, SUM(TotalAmount) AS TotalAmount
FROM Sales.Orders
GROUP BY CustomerID;
GO
CREATE UNIQUE CLUSTERED INDEX IX_vOrdersSummary ON Sales.vOrdersSummary (CustomerID);

#### Stored Procedure Commands

-- Create a stored procedure
CREATE PROCEDURE Sales.GetOrdersByCustomer
    @CustomerID INT
AS
BEGIN
    SELECT OrderID, OrderDate, TotalAmount
    FROM Sales.Orders
    WHERE CustomerID = @CustomerID;
END;

-- Execute a stored procedure
EXEC Sales.GetOrdersByCustomer @CustomerID = 123;

Walk-Through

1

Create a Clustered Index

First, determine the column that will define the physical order of the table. Typically, this is the primary key column, often an integer identity column. Use the CREATE CLUSTERED INDEX statement. SQL Server will sort the existing data rows by the index key and rebuild the table structure into a B-tree. The leaf level becomes the data pages. This operation requires sufficient free space in the database (at least 1.2 times the table size for a full rebuild). During creation, the table is locked with a schema modification (SCH-M) lock, preventing all access. For large tables, this can cause significant downtime. In Azure SQL Database, you can use ONLINE = ON to reduce blocking, though it requires more resources.

2

Create a Nonclustered Index

Identify queries that would benefit from faster lookups — typically those with WHERE clauses on columns that are not the clustered index key. Create the index using CREATE NONCLUSTERED INDEX. The index will be built as a separate B-tree structure. SQL Server scans the table to collect the key values and row locators, sorts them, and writes the index pages. This operation takes a shared lock on the table, allowing reads but blocking writes until the index is fully built. You can specify included columns to make the index covering. For example, if queries always select OrderDate and TotalAmount when filtering by CustomerID, include those columns in the index to avoid key lookups.

3

Create a Standard View

Write a SELECT query that defines the view. Use CREATE VIEW statement. The view is stored as metadata — no data is copied. When a user queries the view, SQL Server expands the view definition into the outer query and optimizes the combined query. For example, if the view joins two tables and the user adds a WHERE clause, SQL Server pushes the filter down to the base tables if possible. Views can be nested up to 32 levels. To avoid breaking changes, use WITH SCHEMABINDING to bind the view to the underlying table schema, preventing modifications to referenced columns.

4

Create an Indexed View

First, create a standard view with SCHEMABINDING. The view must meet strict requirements: no subqueries, no DISTINCT, no TOP, no UNION, no outer joins, and the GROUP BY must include COUNT_BIG(*) if aggregating. Then, create a unique clustered index on the view. SQL Server materializes the view result set into physical storage. When base table data changes, SQL Server automatically updates the indexed view in the same transaction. This can significantly improve performance for reporting queries, but it adds overhead to DML operations. Indexed views are best for static or slowly changing data with frequent read queries.

5

Create and Execute a Stored Procedure

Write the T-SQL statements that form the procedure body, including parameters, variables, and control-of-flow logic. Use CREATE PROCEDURE to store it. On first execution, SQL Server compiles the procedure, creates an execution plan, and caches it. Subsequent executions reuse the plan if parameters are similar. To avoid parameter sniffing issues, you can use WITH RECOMPILE or OPTIMIZE FOR UNKNOWN. Stored procedures can also return output parameters and result sets. They support transaction management, error handling with TRY...CATCH, and can call other procedures. In Azure SQL Database, stored procedures are fully supported and are the recommended way to encapsulate data access logic.

What This Looks Like on the Job

Scenario 1: E-Commerce Order Processing

A large e-commerce platform processes millions of orders daily. The Orders table has over 500 million rows. Without indexes, any search by CustomerID or OrderDate would take minutes. The DBA creates a clustered index on OrderID (the primary key) and a nonclustered index on CustomerID including OrderDate and TotalAmount. This reduces typical customer history queries from 45 seconds to under 100 milliseconds. However, during peak sales events like Black Friday, the high volume of inserts causes page splits on the clustered index because OrderID is sequential — but the clustered index on an identity column is actually optimal because new rows always go to the last page. The real problem is the nonclustered index on CustomerID: each insert also updates that index, causing additional I/O. The DBA monitors index fragmentation and schedules nightly rebuilds during low traffic. In production, they use ONLINE index rebuilds to avoid blocking.

Scenario 2: Financial Reporting with Indexed Views

A bank needs to generate daily summaries of transactions by branch and product type. The raw Transactions table has 2 billion rows, and the summary query aggregates by branch and product, taking 30 minutes. The DBA creates an indexed view with SCHEMABINDING that groups by BranchID and ProductID, with COUNT_BIG(*) and SUM(Amount). A unique clustered index is created on (BranchID, ProductID). Now, the summary query runs instantly because the aggregated data is precomputed. The trade-off: every insert into Transactions now must update the indexed view, adding about 15% overhead to each transaction. The bank accepts this because reporting queries run hundreds of times per day while inserts are continuous but moderate.

Scenario 3: Stored Procedures for API Backend

A SaaS company exposes a REST API that queries a customer database. Instead of allowing direct table access, all data access is through stored procedures. For example, GetCustomerOrders(@CustomerID, @StartDate, @EndDate) encapsulates the join logic between Customers, Orders, and OrderDetails. This provides a security boundary — the application service account only has EXECUTE permission on the stored procedures, not SELECT on the tables. It also centralizes business logic: if the schema changes, only the stored procedure needs updating, not the application code. Performance is improved because the execution plan is cached. The team uses sp_executesql with parameterized queries to prevent SQL injection. They also implement error handling with TRY...CATCH and return custom error codes. In production, they monitor the procedure cache for plan evictions due to memory pressure.

How DP-900 Actually Tests This

What DP-900 Tests on Indexes, Views, and Stored Procedures

DP-900 objective 2.3 expects you to "describe indexes, views, and stored procedures." The exam focuses on conceptual understanding, not syntax. Key areas:

Indexes: Difference between clustered and nonclustered. That a clustered index determines physical order and there can be only one per table. That nonclustered indexes contain pointers to data. That indexes speed up reads but slow down writes. That a table without a clustered index is a heap.

Views: That a view is a virtual table based on a SELECT query. That it does not store data (except indexed views). That views can simplify complex queries and provide security. That indexed views store data physically and improve performance.

Stored Procedures: That they are precompiled T-SQL code. That they improve performance through plan caching. That they can accept parameters and return results. That they enhance security by encapsulating logic.

Common Wrong Answers and Why Candidates Choose Them

1.

"A nonclustered index stores data in sorted order." This is partially correct but misleading. The index is sorted, but the data itself is not physically sorted — only the pointer structure is sorted. Candidates confuse this with clustered indexes.

2.

"Views store data and improve query performance." Only indexed views store data. Standard views are just saved queries. Candidates often think all views are materialized.

3.

"Stored procedures are compiled every time they run." They are compiled on first execution; subsequent runs reuse the cached plan. Candidates may think compilation happens every time.

4.

"You can have multiple clustered indexes on a table." Only one clustered index is allowed because it determines physical order. Candidates might think multiple are possible for different columns.

Exam-Specific Numbers and Terms

Maximum number of nonclustered indexes per table: 999 (though not tested directly, the concept that there can be many is important).

Only one clustered index per table.

SCHEMABINDING is required for indexed views.

Stored procedures can have input and output parameters.

Heaps are tables without clustered indexes.

Edge Cases and Exceptions

Indexed views are not supported in all Azure SQL tiers — check service tier documentation.

Stored procedures can return multiple result sets; functions cannot.

Views cannot have ORDER BY without TOP or OFFSET...FETCH.

Clustered indexes on GUID columns cause fragmentation due to random inserts.

How to Eliminate Wrong Answers

If an answer says a view "stores data," it's wrong unless it specifies "indexed view."

If an answer says indexes "always improve performance," it's wrong — they slow down writes.

If an answer says stored procedures "are compiled every time," it's wrong — they use plan caching.

If an answer claims you can have multiple clustered indexes, it's wrong — only one.

Key Takeaways

A clustered index determines the physical order of data; only one per table.

A nonclustered index is a separate structure with pointers to data; multiple allowed.

Indexes speed up SELECT but slow down INSERT/UPDATE/DELETE due to maintenance overhead.

A view is a virtual table based on a SELECT query; it does not store data unless it is an indexed view.

An indexed view (materialized view) stores data physically and requires SCHEMABINDING.

Stored procedures are precompiled T-SQL code that improve performance through plan caching.

Stored procedures can accept input parameters and return output parameters and result sets.

Stored procedures enhance security by granting EXECUTE without direct table access.

Heaps are tables without a clustered index; data is stored in no particular order.

Indexed views require unique clustered index and COUNT_BIG(*) when using GROUP BY.

Easy to Mix Up

These come up on the exam all the time. Here's how to tell them apart.

Clustered Index

Determines physical order of data rows

Only one per table

Leaf level contains actual data pages

Faster for range queries on the index key

Default for primary key

Nonclustered Index

Does not affect physical order of data

Up to 999 per table

Leaf level contains index key and row locator

Requires a key lookup (or clustered index seek) to retrieve non-key columns

Can include non-key columns to create covering indexes

Watch Out for These

Mistake

A nonclustered index physically reorders the table data.

Correct

Only a clustered index determines the physical order of data. A nonclustered index is a separate structure that contains sorted key values and pointers to the actual rows. The table data itself remains in its original order (clustered index order or heap order).

Mistake

All views automatically improve query performance.

Correct

Standard views do not improve performance — they merely encapsulate a query. Only indexed views (materialized views) store data physically and can speed up queries. In fact, a standard view can sometimes perform worse than the equivalent direct query because it adds complexity.

Mistake

Stored procedures must be recompiled on every execution.

Correct

Stored procedures are compiled on first execution, and the execution plan is cached. Subsequent executions reuse the cached plan unless the schema changes or the plan is evicted due to memory pressure. This is why they improve performance.

Mistake

You can create a clustered index on any column without affecting performance.

Correct

Clustered indexes on columns with random values (like GUIDs) cause frequent page splits and fragmentation, degrading write performance. Best practice is to choose a unique, ever-increasing column like an integer identity column.

Mistake

Indexes are always beneficial and should be created on every column.

Correct

Indexes speed up SELECT queries but slow down INSERT, UPDATE, and DELETE operations because the index must be maintained. Too many indexes can degrade overall performance. Indexes also consume storage. They should be created strategically based on query patterns.

Do You Actually Know This?

Reveal each answer, then mark whether you got it right. Score 60%+ to unlock the next chapter.

Frequently Asked Questions

What is the difference between a clustered and nonclustered index?

A clustered index determines the physical order of data in a table, so there can be only one per table. The leaf level of the index contains the actual data rows. A nonclustered index is a separate structure that contains a sorted list of key values and pointers (row locators) to the data rows. You can have many nonclustered indexes per table. On the DP-900 exam, remember that clustered indexes order the data physically, while nonclustered indexes are like a separate lookup table.

Do views store data in SQL Server?

By default, standard views do not store data — they are virtual tables that execute the underlying SELECT query when accessed. However, an indexed view (created with a unique clustered index) does store the result set physically and is automatically updated when base data changes. The exam often tests this distinction: a view that is not indexed is just a saved query.

What is the purpose of a stored procedure?

A stored procedure is a precompiled set of T-SQL statements that can accept parameters, execute business logic, and return results. They improve performance by caching execution plans, enhance security by allowing granular permissions, and promote code reuse. On the exam, you should know that stored procedures are executed with the EXEC command and can have input and output parameters.

Can I create an index on a view?

Yes, but only if the view meets specific requirements: it must be created with SCHEMABINDING, cannot use certain constructs like DISTINCT or subqueries, and must include COUNT_BIG(*) if using GROUP BY. The first index on a view must be a unique clustered index. Once created, the view becomes an indexed view and stores data physically. This is a common exam topic.

What is a heap in SQL Server?

A heap is a table without a clustered index. Data rows are stored in no particular order, and new rows are inserted at the end of the table. Heaps can be efficient for bulk inserts but are generally slower for lookups because they require a full table scan or a nonclustered index with RID pointers. The DP-900 exam may ask you to identify that a table without a clustered index is a heap.

How do indexes affect write operations?

Indexes slow down INSERT, UPDATE, and DELETE operations because the index structures must be maintained. For each row modification, SQL Server updates all indexes on that table. If there are many indexes, write performance degrades. This is a key trade-off: indexes speed up reads at the cost of slower writes. The exam expects you to understand this balance.

What is parameter sniffing in stored procedures?

Parameter sniffing is when SQL Server uses the parameter values from the first execution to create an optimized execution plan. If subsequent calls use very different parameter values, the cached plan may be suboptimal. This can cause performance issues. Solutions include using WITH RECOMPILE, OPTIMIZE FOR UNKNOWN, or query hints. While not a core DP-900 topic, understanding the concept helps with performance questions.

Terms Worth Knowing

Ready to put this to the test?

You've just covered Indexes, Views, and Stored Procedures — now see how well it sticks with free DP-900 practice questions. Full explanations included, no account needed.

Done with this chapter?