Indexes are database objects that improve query performance by providing faster data retrieval. They work like a book’s index, allowing SQL Server to find data without scanning the entire table.
CREATE CLUSTERED INDEX IX_Orders_OrderID ON Orders(OrderID);
CREATE NONCLUSTERED INDEX IX_Customers_Email ON Customers(Email);
CREATE UNIQUE INDEX UQ_Customers_Phone ON Customers(Phone);
CREATE NONCLUSTERED INDEX IX_Active_Products
ON Products(ProductName)
WHERE Discontinued = 0;
CREATE CLUSTERED COLUMNSTORE INDEX CCI_OrderDetails ON OrderDetails;
CREATE FULLTEXT CATALOG ProductCatalog AS DEFAULT;
CREATE FULLTEXT INDEX ON Products(ProductDescription)
KEY INDEX PK_Products;
CREATE SPATIAL INDEX SIX_Customer_Locations
ON Customers(Location)
USING GEOGRAPHY_AUTO_GRID;
-- Simple non-clustered index
CREATE INDEX IX_Orders_CustomerID ON Orders(CustomerID);
-- Composite index (multiple columns)
CREATE INDEX IX_Orders_DateStatus ON Orders(OrderDate, Status);
-- Index with included columns (covering index)
CREATE INDEX IX_Orders_Covering ON Orders(OrderDate)
INCLUDE (CustomerID, TotalAmount);
-- Rebuild index (fully recreates)
ALTER INDEX IX_Orders_CustomerID ON Orders REBUILD;
-- Reorganize index (defragments)
ALTER INDEX IX_Orders_CustomerID ON Orders REORGANIZE;
-- Disable index
ALTER INDEX IX_Orders_CustomerID ON Orders DISABLE;
-- Drop index
DROP INDEX IX_Orders_CustomerID ON Orders;
-- Before (requires lookup)
SELECT OrderID, CustomerID, OrderDate, TotalAmount
FROM Orders
WHERE OrderDate BETWEEN '2023-01-01' AND '2023-12-31';
-- Create covering index
CREATE INDEX IX_Orders_Covering ON Orders(OrderDate)
INCLUDE (CustomerID, TotalAmount);
-- Good candidate for standard index
CREATE INDEX IX_Customers_Email ON Customers(Email);
-- Leading column should be the range column
CREATE INDEX IX_Orders_Date ON Orders(OrderDate);
-- Index columns in the same order as the sort
CREATE INDEX IX_Products_PriceName ON Products(Price, ProductName);
-- Index join columns on both tables
CREATE INDEX IX_Orders_CustomerID ON Orders(CustomerID);
CREATE INDEX IX_Customers_CustomerID ON Customers(CustomerID);
-- Find unused indexes
SELECT
o.name AS TableName,
i.name AS IndexName,
i.type_desc AS IndexType,
s.user_seeks,
s.user_scans,
s.user_lookups
FROM sys.indexes i
INNER JOIN sys.objects o ON i.object_id = o.object_id
LEFT JOIN sys.dm_db_index_usage_stats s ON i.object_id = s.object_id
AND i.index_id = s.index_id
WHERE o.type = 'U'
AND i.name IS NOT NULL
AND (s.user_seeks = 0 AND s.user_scans = 0 AND s.user_lookups = 0)
ORDER BY o.name, i.name;
-- Query the missing index DMVs
SELECT
migs.avg_total_user_cost * (migs.avg_user_impact / 100.0) * (migs.user_seeks + migs.user_scans) AS improvement_measure,
mid.statement AS table_name,
mid.equality_columns,
mid.inequality_columns,
mid.included_columns
FROM sys.dm_db_missing_index_group_stats migs
INNER JOIN sys.dm_db_missing_index_groups mig ON migs.group_handle = mig.index_group_handle
INNER JOIN sys.dm_db_missing_index_details mid ON mig.index_handle = mid.index_handle
ORDER BY improvement_measure DESC;
-- Create schema-bound view
CREATE VIEW dbo.vw_OrderSummary WITH SCHEMABINDING AS
SELECT
CustomerID,
COUNT_BIG(*) AS OrderCount,
SUM(TotalAmount) AS TotalSpent
FROM dbo.Orders
GROUP BY CustomerID;
GO
-- Create clustered index on view
CREATE UNIQUE CLUSTERED INDEX IX_vw_OrderSummary
ON dbo.vw_OrderSummary(CustomerID);
-- Create partition function
CREATE PARTITION FUNCTION pf_OrderDateRange (datetime)
AS RANGE RIGHT FOR VALUES
('2020-01-01', '2021-01-01', '2022-01-01', '2023-01-01');
GO
-- Create partition scheme
CREATE PARTITION SCHEME ps_OrderDateRange
AS PARTITION pf_OrderDateRange
TO (fg_2019, fg_2020, fg_2021, fg_2022, fg_2023);
GO
-- Create partitioned index
CREATE CLUSTERED INDEX IX_Orders_OrderDate
ON Orders(OrderDate)
ON ps_OrderDateRange(OrderDate);
-- Only index non-NULL values
CREATE INDEX IX_Orders_DiscountCode ON Orders(DiscountCode)
WHERE DiscountCode IS NOT NULL;
-- Rebuild if fragmentation > 30%
ALTER INDEX ALL ON Orders REBUILD WITH (ONLINE = ON);
-- Reorganize if fragmentation between 5-30%
ALTER INDEX ALL ON Orders REORGANIZE;
UPDATE STATISTICS Orders WITH FULLSCAN;
Schedule index maintenance during low-usage periods
Consider ONLINE operations for high-availability systems
CREATE INDEX IX_Orders_CustomerID ON Orders(CustomerID) WITH (ONLINE = ON);
-- Scenario: E-commerce database optimization
-- 1. Clustered index on Orders table (natural key)
CREATE CLUSTERED INDEX PK_Orders ON Orders(OrderID);
-- 2. Non-clustered indexes for common searches
CREATE INDEX IX_Orders_CustomerID ON Orders(CustomerID);
CREATE INDEX IX_Orders_OrderDate ON Orders(OrderDate);
-- 3. Covering index for frequent order listing query
CREATE INDEX IX_Orders_CustomerDateStatus ON Orders(CustomerID, OrderDate)
INCLUDE (Status, TotalAmount);
-- 4. Filtered index for active products
CREATE INDEX IX_Products_Active ON Products(ProductName, Price)
WHERE Discontinued = 0 AND StockQuantity > 0;
-- 5. Index for search operations
CREATE FULLTEXT CATALOG ProductSearch AS DEFAULT;
CREATE FULLTEXT INDEX ON Products(ProductName, Description)
KEY INDEX PK_Products;
-- 6. Columnstore index for analytics
CREATE CLUSTERED COLUMNSTORE INDEX CCI_OrderDetails ON OrderDetails;
By implementing these indexing strategies, you can dramatically improve query performance in your T-SQL applications. Remember to: