Overview
Heaps (Tables without Clustered Indexes)
Clustered and Nonclustered Indexes Described
Create Clustered Indexes
Create Nonclustered Indexes
Create Unique Indexes
Create Filtered Indexes
Create Indexes with Included Columns
Delete an Index
Modify an Index
Move an Existing Index to a Different Filegroup
Indexes on Computed Columns
SORT_IN_TEMPDB Option For Indexes
Disable Indexes and Constraints
Enable Indexes and Constraints
Rename Indexes
Set Index Options
Disk Space Requirements for Index DDL Operations
Transaction Log Disk Space for Index Operations
Index Disk Space Example
Reorganize and Rebuild Indexes
Specify Fill Factor for an Index
Perform Index Operations Online
How Online Index Operations Work
Guidelines for Online Index Operations
Configure Parallel Index Operations
Index Properties F1 Help
Columnstore indexes
Overview
Architecture
Design guidance
Data loading guidance
What's new
Query performance
Real-time operational analytics
Data warehouse data warehouse
Defragment
Indexes
3/24/2017 • 3 min to read • Edit Online
THIS TOPIC APPLIES TO: SQL Server (starting with 2016) Azure SQL Database Azure SQL Data
Warehouse Parallel Data Warehouse
The following table lists the types of indexes available in SQL Server and provides links to additional information.
Hash With a hash index, data is accessed Guidelines for Using Indexes on
through an in-memory hash table. Hash Memory-Optimized Tables
indexes consume a fixed amount of
memory, which is a function of the
bucket count.
Clustered A clustered index sorts and stores the Clustered and Nonclustered Indexes
data rows of the table or view in order Described
based on the clustered index key. The
clustered index is implemented as a B- Create Clustered Indexes
tree index structure that supports fast
retrieval of the rows, based on their
clustered index key values.
Unique A unique index ensures that the index Create Unique Indexes
key contains no duplicate values and
therefore every row in the table or view
is in some way unique.
Index with included columns A nonclustered index that is extended Create Indexes with Included Columns
to include nonkey columns in addition
to the key columns.
Index on computed columns An index on a column that is derived Indexes on Computed Columns
from the value of one or more other
columns, or certain deterministic inputs.
Related Tasks
Related Content
SORT_IN_TEMPDB Option For Indexes
Disable Indexes and Constraints
Enable Indexes and Constraints
Rename Indexes
Set Index Options
Disk Space Requirements for Index DDL Operations
Reorganize and Rebuild Indexes
Specify Fill Factor for an Index
Pages and Extents Architecture Guide
See Also
Clustered and Nonclustered Indexes Described
Heaps (Tables without Clustered Indexes)
3/24/2017 • 4 min to read • Edit Online
THIS TOPIC APPLIES TO: SQL Server (starting with 2016) Azure SQL Database Azure SQL Data
Warehouse Parallel Data Warehouse
A heap is a table without a clustered index. One or more nonclustered indexes can be created on tables stored as a
heap. Data is stored in the heap without specifying an order. Usually data is initially stored in the order in which is
the rows are inserted into the table, but the Database Engine can move data around in the heap to store the rows
efficiently; so the data order cannot be predicted. To guarantee the order of rows returned from a heap, you must
use the ORDER BY clause. To specify the order for storage of the rows, create a clustered index on the table, so that
the table is not a heap.
NOTE
There are sometimes good reasons to leave a table as a heap instead of creating a clustered index, but using heaps effectively
is an advanced skill. Most tables should have a carefully chosen clustered index unless a good reason exists for leaving the
table as a heap.
Managing Heaps
To create a heap, create a table without a clustered index. If a table already has a clustered index, drop the clustered
index to return the table to a heap.
To remove a heap, create a clustered index on the heap.
To rebuild a heap to reclaim wasted space, create a clustered index on the heap, and then drop that clustered index.
WARNING
Creating or dropping clustered indexes requires rewriting the entire table. If the table has nonclustered indexes, all the
nonclustered indexes must all be recreated whenever the clustered index is changed. Therefore, changing from a heap to a
clustered index structure or back can take a lot of time and require disk space for reordering data in tempdb.
Heap Structures
A heap is a table without a clustered index. Heaps have one row in sys.partitions, with index_id = 0 for each
partition used by the heap. By default, a heap has a single partition. When a heap has multiple partitions, each
partition has a heap structure that contains the data for that specific partition. For example, if a heap has four
partitions, there are four heap structures; one in each partition.
Depending on the data types in the heap, each heap structure will have one or more allocation units to store and
manage the data for a specific partition. At a minimum, each heap will have one IN_ROW_DATA allocation unit per
partition. The heap will also have one LOB_DATA allocation unit per partition, if it contains large object (LOB)
columns. It will also have one ROW_OVERFLOW_DATA allocation unit per partition, if it contains variable length columns
that exceed the 8,060 byte row size limit.
The column first_iam_page in the sys.system_internals_allocation_units system view points to the first IAM page
in the chain of IAM pages that manage the space allocated to the heap in a specific partition. SQL Server uses the
IAM pages to move through the heap. The data pages and the rows within them are not in any specific order and
are not linked. The only logical connection between data pages is the information recorded in the IAM pages.
IMPORTANT
The sys.system_internals_allocation_units system view is reserved for Microsoft SQL Server internal use only. Future
compatibility is not guaranteed.
Table scans or serial reads of a heap can be performed by scanning the IAM pages to find the extents that are
holding pages for the heap. Because the IAM represents extents in the same order that they exist in the data files,
this means that serial heap scans progress sequentially through each file. Using the IAM pages to set the scan
sequence also means that rows from the heap are not typically returned in the order in which they were inserted.
The following illustration shows how the SQL Server Database Engine uses IAM pages to retrieve data rows in a
single partition heap.
Related Content
CREATE INDEX (Transact-SQL)
DROP INDEX (Transact-SQL)
Clustered and Nonclustered Indexes Described
Clustered and Nonclustered Indexes Described
3/24/2017 • 3 min to read • Edit Online
THIS TOPIC APPLIES TO: SQL Server (starting with 2016) Azure SQL Database Azure SQL Data
Warehouse Parallel Data Warehouse
An index is an on-disk structure associated with a table or view that speeds retrieval of rows from the table or
view. An index contains keys built from one or more columns in the table or view. These keys are stored in a
structure (B-tree) that enables SQL Server to find the row or rows associated with the key values quickly and
efficiently.
A table or view can contain the following types of indexes:
Clustered
Clustered indexes sort and store the data rows in the table or view based on their key values. These
are the columns included in the index definition. There can be only one clustered index per table,
because the data rows themselves can be sorted in only one order.
The only time the data rows in a table are stored in sorted order is when the table contains a
clustered index. When a table has a clustered index, the table is called a clustered table. If a table has
no clustered index, its data rows are stored in an unordered structure called a heap.
Nonclustered
Nonclustered indexes have a structure separate from the data rows. A nonclustered index contains
the nonclustered index key values and each key value entry has a pointer to the data row that
contains the key value.
The pointer from an index row in a nonclustered index to a data row is called a row locator. The
structure of the row locator depends on whether the data pages are stored in a heap or a clustered
table. For a heap, a row locator is a pointer to the row. For a clustered table, the row locator is the
clustered index key.
You can add nonkey columns to the leaf level of the nonclustered index to by-pass existing index key
limits, 900 bytes and 16 key columns, and execute fully covered, indexed, queries. For more
information, see Create Indexes with Included Columns.
Both clustered and nonclustered indexes can be unique. This means no two rows can have the same value
for the index key. Otherwise, the index is not unique and multiple rows can share the same key value. For
more information, see Create Unique Indexes.
Indexes are automatically maintained for a table or view whenever the table data is modified.
See Indexes for additional types of special purpose indexes.
Related Tasks
Create Clustered Indexes
Create Nonclustered Indexes
Create Clustered Indexes
3/24/2017 • 4 min to read • Edit Online
THIS TOPIC APPLIES TO: SQL Server (starting with 2016) Azure SQL Database Azure SQL Data
Warehouse Parallel Data Warehouse
You can create clustered indexes on tables by using SQL Server Management Studio or Transact-SQL. With few
exceptions, every table should have a clustered index. Besides improving query performance, a clustered index can
be rebuilt or reorganized on demand to control table fragmentation. A clustered index can also be created on a
view. (Clustered indexes are defined in the topic Clustered and Nonclustered Indexes Described.)
In This Topic
Before you begin:
Typical Implementations
Limitations and Restrictions
Security
To create a clustered index on a table, using:
SQL Server Management Studio
Transact-SQL
USE AdventureWorks2012;
GO
-- Create a new table with three columns.
CREATE TABLE dbo.TestTable
(TestCol1 int NOT NULL,
TestCol2 nchar(10) NULL,
TestCol3 nvarchar(50) NULL);
GO
-- Create a clustered index called IX_TestTable_TestCol1
-- on the dbo.TestTable table using the TestCol1 column.
CREATE CLUSTERED INDEX IX_TestTable_TestCol1
ON dbo.TestTable (TestCol1);
GO
See Also
Create Primary Keys
Create Unique Constraints
Create Nonclustered Indexes
3/24/2017 • 3 min to read • Edit Online
THIS TOPIC APPLIES TO: SQL Server (starting with 2016) Azure SQL Database Azure SQL Data
Warehouse Parallel Data Warehouse
You can create nonclustered indexes in SQL Server 2016 by using SQL Server Management Studio or Transact-
SQL. A nonclustered index is an index structure separate from the data stored in a table that reorders one or more
selected columns. Nonclustered indexes can often help you find data more quickly than searching the underlying
table; queries can sometimes be answered entirely by the data in the nonclustered index, or the nonclustered index
can point the Database Engine to the rows in the underlying table. Generally, nonclustered indexes are created to
improve the performance of frequently used queries not covered by the clustered index or to locate rows in a table
without a clustered index (called a heap). You can create multiple nonclustered indexes on a table or indexed view.
In This Topic
Before you begin:
Typical Implementations
Security
To create a nonclustered index, using:
SQL Server Management Studio
Transact-SQL
Using Transact-SQL
To create a nonclustered index on a table
1. In Object Explorer, connect to an instance of Database Engine.
2. On the Standard bar, click New Query.
3. Copy and paste the following example into the query window and click Execute.
USE AdventureWorks2012;
GO
-- Find an existing index named IX_ProductVendor_VendorID and delete it if found.
IF EXISTS (SELECT name FROM sys.indexes
WHERE name = N'IX_ProductVendor_VendorID')
DROP INDEX IX_ProductVendor_VendorID ON Purchasing.ProductVendor;
GO
-- Create a nonclustered index called IX_ProductVendor_VendorID
-- on the Purchasing.ProductVendor table using the BusinessEntityID column.
CREATE NONCLUSTERED INDEX IX_ProductVendor_VendorID
ON Purchasing.ProductVendor (BusinessEntityID);
GO
THIS TOPIC APPLIES TO: SQL Server (starting with 2016) Azure SQL Database Azure SQL Data
Warehouse Parallel Data Warehouse
This topic describes how to create a unique index on a table in SQL Server 2016 by using SQL Server Management
Studio or Transact-SQL. A unique index guarantees that the index key contains no duplicate values and therefore
every row in the table is in some way unique. There are no significant differences between creating a UNIQUE
constraint and creating a unique index that is independent of a constraint. Data validation occurs in the same
manner, and the query optimizer does not differentiate between a unique index created by a constraint or
manually created. However, creating a UNIQUE constraint on the column makes the objective of the index clear.
For more information on UNIQUE constraints, see Unique Constraints and Check Constraints.
When you create a unique index, you can set an option to ignore duplicate keys. If this option is set to Yes and you
attempt to create duplicate keys by adding data that affects multiple rows (with the INSERT statement), the row
containing a duplicate is not added. If it is set to No, the entire insert operation fails and all the data is rolled back.
NOTE
You cannot create a unique index on a single column if that column contains NULL in more than one row. Similarly, you
cannot create a unique index on multiple columns if the combination of columns contains NULL in more than one row. These
are treated as duplicate values for indexing purposes.
In This Topic
Before you begin:
Benefits of a Unique Index
Typical Implementations
Limitations and Restrictions
Security
To create a unique index on a table, using:
SQL Server Management Studio
Transact-SQL
Using Transact-SQL
To create a unique index on a table
1. In Object Explorer, connect to an instance of Database Engine.
2. On the Standard bar, click New Query.
3. Copy and paste the following example into the query window and click Execute.
USE AdventureWorks2012;
GO
-- Find an existing index named AK_UnitMeasure_Name and delete it if found
IF EXISTS (SELECT name from sys.indexes
WHERE name = N'AK_UnitMeasure_Name')
DROP INDEX AK_UnitMeasure_Name ON Production.UnitMeasure;
GO
-- Create a unique index called AK_UnitMeasure_Name
-- on the Production.UnitMeasure table using the Name column.
CREATE UNIQUE INDEX AK_UnitMeasure_Name
ON Production.UnitMeasure (Name);
GO
For more information, see CREATE INDEX (Transact-SQL).
Create Filtered Indexes
3/24/2017 • 6 min to read • Edit Online
THIS TOPIC APPLIES TO: SQL Server (starting with 2016) Azure SQL Database Azure SQL Data
Warehouse Parallel Data Warehouse
This topic describes how to create a filtered index in SQL Server 2016 by using SQL Server Management Studio or
Transact-SQL. A filtered index is an optimized nonclustered index especially suited to cover queries that select from
a well-defined subset of data. It uses a filter predicate to index a portion of rows in the table. A well-designed
filtered index can improve query performance as well as reduce index maintenance and storage costs compared
with full-table indexes.
Filtered indexes can provide the following advantages over full-table indexes:
Improved query performance and plan quality
A well-designed filtered index improves query performance and execution plan quality because it is smaller
than a full-table nonclustered index and has filtered statistics. The filtered statistics are more accurate than
full-table statistics because they cover only the rows in the filtered index.
Reduced index maintenance costs
An index is maintained only when data manipulation language (DML) statements affect the data in the index.
A filtered index reduces index maintenance costs compared with a full-table nonclustered index because it is
smaller and is only maintained when the data in the index is changed. It is possible to have a large number
of filtered indexes, especially when they contain data that is changed infrequently. Similarly, if a filtered
index contains only the frequently modified data, the smaller size of the index reduces the cost of updating
the statistics.
Reduced index storage costs
Creating a filtered index can reduce disk storage for nonclustered indexes when a full-table index is not
necessary. You can replace a full-table nonclustered index with multiple filtered indexes without significantly
increasing the storage requirements.
In This Topic
Before you begin:
Design Considerations
Limitations and Restrictions
Security
To create a filtered index, using:
SQL Server Management Studio
Transact-SQL
Using Transact-SQL
To create a filtered index
1. In Object Explorer, connect to an instance of Database Engine.
2. On the Standard bar, click New Query.
3. Copy and paste the following example into the query window and click Execute.
USE AdventureWorks2012;
GO
-- Looks for an existing filtered index named "FIBillOfMaterialsWithEndDate"
-- and deletes it from the table Production.BillOfMaterials if found.
IF EXISTS (SELECT name FROM sys.indexes
WHERE name = N'FIBillOfMaterialsWithEndDate'
AND object_id = OBJECT_ID (N'Production.BillOfMaterials'))
DROP INDEX FIBillOfMaterialsWithEndDate
ON Production.BillOfMaterials
GO
-- Creates a filtered index "FIBillOfMaterialsWithEndDate"
-- on the table Production.BillOfMaterials
-- using the columms ComponentID and StartDate.
The filtered index above is valid for the following query. You can display the query execution plan to
determine if the query optimizer used the filtered index.
USE AdventureWorks2012;
GO
SELECT ProductAssemblyID, ComponentID, StartDate
FROM Production.BillOfMaterials
WHERE EndDate IS NOT NULL
AND ComponentID = 5
AND StartDate > '01/01/2008' ;
GO
USE AdventureWorks2012;
GO
SELECT ComponentID, StartDate FROM Production.BillOfMaterials
WITH ( INDEX ( FIBillOfMaterialsWithEndDate ) )
WHERE EndDate IN ('20000825', '20000908', '20000918');
GO
THIS TOPIC APPLIES TO: SQL Server (starting with 2016) Azure SQL Database Azure SQL Data
Warehouse Parallel Data Warehouse
This topic describes how to add included (or nonkey) columns to extend the functionality of nonclustered indexes
in SQL Server by using SQL Server Management Studio or Transact-SQL. By including nonkey columns, you can
create nonclustered indexes that cover more queries. This is because the nonkey columns have the following
benefits:
They can be data types not allowed as index key columns.
They are not considered by the Database Engine when calculating the number of index key columns or
index key size.
An index with nonkey columns can significantly improve query performance when all columns in the query
are included in the index either as key or nonkey columns. Performance gains are achieved because the
query optimizer can locate all the column values within the index; table or clustered index data is not
accessed resulting in fewer disk I/O operations.
NOTE
When an index contains all the columns referenced by a query it is typically referred to as covering the query.
In This Topic
Before you begin:
Design Recommendations
Limitations and Restrictions
Security
To create an index with nonkey columns, using:
SQL Server Management Studio
Transact-SQL
Using Transact-SQL
To create an index with nonkey columns
1. In Object Explorer, connect to an instance of Database Engine.
2. On the Standard bar, click New Query.
3. Copy and paste the following example into the query window and click Execute.
USE AdventureWorks2012;
GO
-- Creates a nonclustered index on the Person.Address table with four included (nonkey) columns.
-- index key column is PostalCode and the nonkey columns are
-- AddressLine1, AddressLine2, City, and StateProvinceID.
CREATE NONCLUSTERED INDEX IX_Address_PostalCode
ON Person.Address (PostalCode)
INCLUDE (AddressLine1, AddressLine2, City, StateProvinceID);
GO
THIS TOPIC APPLIES TO: SQL Server (starting with 2016) Azure SQL Database Azure SQL Data
Warehouse Parallel Data Warehouse
This topic describes how to delete (drop) an index in SQL Server 2016 by using SQL Server Management Studio or
Transact-SQL.
In This Topic
Before you begin:
Limitations and Restrictions
Security
To delete an index, using:
SQL Server Management Studio
Transact-SQL
Using Transact-SQL
To delete an index
1. In Object Explorer, connect to an instance of Database Engine.
2. On the Standard bar, click New Query.
3. Copy and paste the following example into the query window and click Execute.
USE AdventureWorks2012;
GO
-- delete the IX_ProductVendor_BusinessEntityID index
-- from the Purchasing.ProductVendor table
DROP INDEX IX_ProductVendor_BusinessEntityID
ON Purchasing.ProductVendor;
GO
THIS TOPIC APPLIES TO: SQL Server (starting with 2016) Azure SQL Database Azure SQL Data
Warehouse Parallel Data Warehouse
This topic describes how to modify an index in SQL Server 2016 by using SQL Server Management Studio or
Transact-SQL.
IMPORTANT
Indexes created as the result of a PRIMARY KEY or UNIQUE constraint cannot be modified by using this method. Instead, the
constraint must be modified.
In This Topic
To modify an index, using:
SQL Server Management Studio
Transact-SQL
Using Transact-SQL
To modify an index
1. Connect to the Database Engine.
2. From the Standard bar, click New Query.
3. Copy and paste the following example into the query window and click Execute. This example drops and re-
creates an existing index on the ProductID column of the Production.WorkOrder table by using the
DROP_EXISTING option. The options FILLFACTOR and PAD_INDEX are also set.
USE AdventureWorks2012;
GO
CREATE NONCLUSTERED INDEX IX_WorkOrder_ProductID
ON Production.WorkOrder(ProductID)
WITH (FILLFACTOR = 80,
PAD_INDEX = ON,
DROP_EXISTING = ON);
GO
The following example uses ALTER INDEX to set several options on the index
AK_SalesOrderHeader_SalesOrderNumber .
USE AdventureWorks2012;
GO
ALTER INDEX AK_SalesOrderHeader_SalesOrderNumber ON
Sales.SalesOrderHeader
SET (
STATISTICS_NORECOMPUTE = ON,
IGNORE_DUP_KEY = ON,
ALLOW_PAGE_LOCKS = ON
) ;
GO
See Also
CREATE INDEX (Transact-SQL)
ALTER INDEX (Transact-SQL)
INDEXPROPERTY (Transact-SQL)
sys.indexes (Transact-SQL)
sys.index_columns (Transact-SQL)
Set Index Options
Rename Indexes
Move an Existing Index to a Different Filegroup
3/24/2017 • 5 min to read • Edit Online
THIS TOPIC APPLIES TO: SQL Server (starting with 2016) Azure SQL Database Azure SQL Data
Warehouse Parallel Data Warehouse
This topic describes how to move an existing index from its current filegroup to a different filegroup in SQL Server
2016 by using SQL Server Management Studio or Transact-SQL.
In This Topic
Before you begin:
Limitations and Restrictions
Security
To move an existing index to a different filegroup, using:
SQL Server Management Studio
Transact-SQL
NOTE
If the table column is a computed column, Column Data Type displays "computed column."
NOTE
This option is not available for XML indexes, or if the index is a disabled clustered index.
NOTE
If a value greater than the number of available CPUs is specified, the actual number of available CPUs is used.
Using Transact-SQL
To move an existing index to a different filegroup
1. In Object Explorer, connect to an instance of Database Engine.
2. On the Standard bar, click New Query.
3. Copy and paste the following example into the query window and click Execute.
USE AdventureWorks2012;
GO
-- Creates the TransactionsFG1 filegroup on the AdventureWorks2012 database
ALTER DATABASE AdventureWorks2012
ADD FILEGROUP TransactionsFG1;
GO
/* Adds the TransactionsFG1dat3 file to the TransactionsFG1 filegroup. Please note that you will have to
change the filename parameter in this statement to execute it without errors.
*/
ALTER DATABASE AdventureWorks2012
ADD FILE
(
NAME = TransactionsFG1dat3,
FILENAME = 'C:\Program Files\Microsoft SQL Server\MSSQL13\MSSQL\DATA\TransactionsFG1dat3.ndf',
SIZE = 5MB,
MAXSIZE = 100MB,
FILEGROWTH = 5MB
)
TO FILEGROUP TransactionsFG1;
GO
/*Creates the IX_Employee_OrganizationLevel_OrganizationNode index
on the TransactionsPS1 filegroup and drops the original IX_Employee_OrganizationLevel_OrganizationNode
index.
*/
CREATE NONCLUSTERED INDEX IX_Employee_OrganizationLevel_OrganizationNode
ON HumanResources.Employee (OrganizationLevel, OrganizationNode)
WITH (DROP_EXISTING = ON)
ON TransactionsFG1;
GO
THIS TOPIC APPLIES TO: SQL Server (starting with 2016) Azure SQL Database Azure SQL Data
Warehouse Parallel Data Warehouse
You can define indexes on computed columns as long as the following requirements are met:
Ownership requirements
Determinism requirements
Precision requirements
Data type requirements
SET option requirements
Ownership Requirements
All function references in the computed column must have the same owner as the table.
Determinism Requirements
IMPORTANT
Expressions are deterministic if they always return the same result for a specified set of inputs. The IsDeterministic property
of the COLUMNPROPERTY function reports whether a computed_column_expression is deterministic.
Precision Requirements
The computed_column_expression must be precise. A computed_column_expression is precise when one or more
of the following is true:
It is not an expression of the float or real data types.
It does not use a float or real data type in its definition. For example, in the following statement, column y
is int and deterministic but not precise.
NOTE
Any float or real expression is considered imprecise and cannot be a key of an index; a float or real expression can be used
in an indexed view but not as a key. This is true also for computed columns. Any function, expression, or user-defined
function is considered imprecise if it contains any float or real expressions. This includes logical ones (comparisons).
Related Content
COLUMNPROPERTY (Transact-SQL)
SORT_IN_TEMPDB Option For Indexes
4/29/2017 • 8 min to read • Edit Online
THIS TOPIC APPLIES TO: SQL Server (starting with 2016) Azure SQL Database Azure SQL Data
Warehouse Parallel Data Warehouse
When you create or rebuild an index, by setting the SORT_IN_TEMPDB option to ON you can direct the SQL Server
Database Engine to use tempdb to store the intermediate sort results that are used to build the index. Although
this option increases the amount of temporary disk space that is used to create an index, the option could reduce
the time that is required to create or rebuild an index when tempdb is on a set of disks different from that of the
user database. For more information about tempdb, see Configure the index create memory Server Configuration
Option.
SORT_IN_TEMPDB Option
When SORT_IN_TEMPDB is set to OFF, the default, the sort runs are stored in the destination filegroup. During the
first phase of creating the index, the alternating reads of the base table pages and writes of the sort runs move the
disk read/write heads from one area of the disk to another. The heads are in the data page area as the data pages
are scanned. They move to an area of free space when the sort buffers fill and the current sort run has to be written
to disk, and then move back to the data page area as the table page scan is resumed. The read/write head
movement is greater in the second phase. At that time the sort process is typically alternating reads from each sort
run area. Both the sort runs and the new index pages are built in the destination filegroup. This means that at the
same time the Database Engine is spreading reads across the sort runs, it has to periodically jump to the index
extents to write new index pages as they are filled.
If the SORT_IN_TEMPDB option is set to ON and tempdb is on a separate set of disks from the destination
filegroup, during the first phase, the reads of the data pages occur on a different disk from the writes to the sort
work area in tempdb. This means the disk reads of the data keys generally continue more serially across the disk,
and the writes to the tempdb disk also are generally serial, as do the writes to build the final index. Even if other
users are using the database and accessing separate disk addresses, the overall pattern of reads and writes are
more efficient when SORT_IN_TEMPDB is specified than when it is not.
The SORT_IN_TEMPDB option may improve the contiguity of index extents, especially if the CREATE INDEX
operation is not being processed in parallel. The sort work area extents are freed on a somewhat random basis
with regard to their location in the database. If the sort work areas are contained in the destination filegroup, as the
sort work extents are freed, they can be acquired by the requests for extents to hold the index structure as it is built.
This can randomize the locations of the index extents to a degree. If the sort extents are held separately in tempdb,
the sequence in which they are freed has no effect on the location of the index extents. Also, when the intermediate
sort runs are stored in tempdb instead of the destination filegroup, there is more space available in the destination
filegroup. This increases the chances that index extents will be contiguous.
The SORT_IN_TEMPDB option affects only the current statement. No metadata records that the index was or was
not sorted in tempdb. For example, if you create a nonclustered index using the SORT_IN_TEMPDB option, and at a
later time create a clustered index without specifying the option, the Database Engine does not use the option
when it re-creates the nonclustered index.
NOTE
If a sort operation is not required or if the sort can be performed in memory, the SORT_IN_TEMPDB option is ignored.
Related Tasks
CREATE INDEX (Transact-SQL)
Reorganize and Rebuild Indexes
Related Content
ALTER INDEX (Transact-SQL)
Configure the index create memory Server Configuration Option
Disk Space Requirements for Index DDL Operations
Disable Indexes and Constraints
3/24/2017 • 5 min to read • Edit Online
THIS TOPIC APPLIES TO: SQL Server (starting with 2016) Azure SQL Database Azure SQL Data
Warehouse Parallel Data Warehouse
This topic describes how to disable an index or constraints in SQL Server 2016 by using SQL Server Management
Studio or Transact-SQL. Disabling an index prevents user access to the index, and for clustered indexes to the
underlying table data. The index definition remains in metadata, and index statistics are kept on nonclustered
indexes. Disabling a nonclustered or clustered index on a view physically deletes the index data. Disabling a
clustered index on a table prevents access to the data; the data still remains in the table, but is unavailable for data
manipulation language (DML) operations until the index is dropped or rebuilt.
In This Topic
Before you begin:
Limitations and Restrictions
Security
To disable an index, using:
SQL Server Management Studio
Transact-SQL
Using Transact-SQL
To disable an index
1. In Object Explorer, connect to an instance of Database Engine.
2. On the Standard bar, click New Query.
3. Copy and paste the following example into the query window and click Execute.
USE AdventureWorks2012;
GO
-- disables the IX_Employee_OrganizationLevel_OrganizationNode index
-- on the HumanResources.Employee table
ALTER INDEX IX_Employee_OrganizationLevel_OrganizationNode ON HumanResources.Employee
DISABLE;
USE AdventureWorks2012;
GO
-- Disables all indexes on the HumanResources.Employee table.
ALTER INDEX ALL ON HumanResources.Employee
DISABLE;
THIS TOPIC APPLIES TO: SQL Server (starting with 2016) Azure SQL Database Azure SQL Data
Warehouse Parallel Data Warehouse
This topic describes how to enable a disabled index in SQL Server 2016 by using SQL Server Management Studio
or Transact-SQL. After an index is disabled, it remains in a disabled state until it is rebuilt or dropped
In This Topic
Before you begin:
Limitations and Restrictions
Security
To enable a disabled index, using:
SQL Server Management Studio
Transact-SQL
Creating a new clustered index, behaves the same as ALTER INDEX ALL REBUILD.
Allowed actions on nonclustered indexes associated with a clustered index depend on the state, whether
disabled or enabled, of both index types. The following table summarizes the allowed actions on
nonclustered indexes.
WHEN BOTH THE CLUSTERED AND WHEN THE CLUSTERED INDEX IS
NONCLUSTERED INDEXES ARE ENABLED AND THE NONCLUSTERED
NONCLUSTERED INDEX ACTION DISABLED. INDEX IS IN EITHER STATE.
Security
Permissions
Requires ALTER permission on the table or view. If using DBCC DBREINDEX, eser must either own the table or be a
member of the sysadmin fixed server role or the db_ddladmin and db_owner fixed database roles.
Using Transact-SQL
To enable a disabled index using ALTER INDEX
1. In Object Explorer, connect to an instance of Database Engine.
2. On the Standard bar, click New Query.
3. Copy and paste the following example into the query window and click Execute.
USE AdventureWorks2012;
GO
-- Enables the IX_Employee_OrganizationLevel_OrganizationNode index
-- on the HumanResources.Employee table.
USE AdventureWorks2012;
GO
-- re-creates the IX_Employee_OrganizationLevel_OrganizationNode index
-- on the HumanResources.Employee table
-- using the OrganizationLevel and OrganizationNode columns
-- and then deletes the existing IX_Employee_OrganizationLevel_OrganizationNode index
CREATE INDEX IX_Employee_OrganizationLevel_OrganizationNode ON HumanResources.Employee
(OrganizationLevel, OrganizationNode)
WITH (DROP_EXISTING = ON);
GO
USE AdventureWorks2012;
GO
-- enables the IX_Employee_OrganizationLevel_OrganizationNode index
-- on the HumanResources.Employee table
DBCC DBREINDEX ("HumanResources.Employee", IX_Employee_OrganizationLevel_OrganizationNode);
GO
USE AdventureWorks2012;
GO
-- enables all indexes
-- on the HumanResources.Employee table
ALTER INDEX ALL ON HumanResources.Employee
REBUILD;
GO
USE AdventureWorks2012;
GO
-- enables all indexes
-- on the HumanResources.Employee table
DBCC DBREINDEX ("HumanResources.Employee", " ");
GO
For more information, see ALTER INDEX (Transact-SQL), CREATE INDEX (Transact-SQL), and DBCC
DBREINDEX (Transact-SQL).
Rename Indexes
3/24/2017 • 2 min to read • Edit Online
THIS TOPIC APPLIES TO: SQL Server (starting with 2016) Azure SQL Database Azure SQL Data
Warehouse Parallel Data Warehouse
This topic describes how to rename an index in SQL Server 2016 by using SQL Server Management Studio or
Transact-SQL. Renaming an index replaces the current index name with the new name that you provide. The
specified name must be unique within the table or view. For example, two tables can have an index named XPK_1,
but the same table cannot have two indexes named XPK_1. You cannot create an index with the same name as an
existing disabled index. Renaming an index does not cause the index to be rebuilt.
In This Topic
Before you begin:
Limitations and Restrictions
Security
To rename an index, using:
SQL Server Management Studio
Transact-SQL
Using Transact-SQL
To rename an index
1. In Object Explorer, connect to an instance of Database Engine.
2. On the Standard bar, click New Query.
3. Copy and paste the following example into the query window and click Execute.
USE AdventureWorks2012;
GO
--Renames the IX_ProductVendor_VendorID index on the Purchasing.ProductVendor table to IX_VendorID.
THIS TOPIC APPLIES TO: SQL Server (starting with 2016) Azure SQL Database Azure SQL Data
Warehouse Parallel Data Warehouse
This topic describes how to modify the properties of an index in SQL Server 2016 by using SQL Server
Management Studio or Transact-SQL.
In This Topic
Before you begin:
Limitations and Restrictions
Security
To modify the properties of an index, using:
SQL Server Management Studio
Transact-SQL
Using Transact-SQL
To see the properties of all the indexes in a table
1. In Object Explorer, connect to an instance of Database Engine.
2. On the Standard bar, click New Query.
3. Copy and paste the following example into the query window and click Execute.
USE AdventureWorks2012;
GO
SELECT i.name AS index_name,
i.type_desc,
i.is_unique,
ds.type_desc AS filegroup_or_partition_scheme,
ds.name AS filegroup_or_partition_scheme_name,
i.ignore_dup_key,
i.is_primary_key,
i.is_unique_constraint,
i.fill_factor,
i.is_padded,
i.is_disabled,
i.allow_row_locks,
i.allow_page_locks,
i.has_filter,
i.filter_definition
FROM sys.indexes AS i
INNER JOIN sys.data_spaces AS ds ON i.data_space_id = ds.data_space_id
WHERE is_hypothetical = 0 AND i.index_id <> 0
AND i.object_id = OBJECT_ID('HumanResources.Employee');
GO
USE AdventureWorks2012;
GO
ALTER INDEX ALL ON Production.Product
REBUILD WITH (FILLFACTOR = 80, SORT_IN_TEMPDB = ON,
STATISTICS_NORECOMPUTE = ON);
GO
THIS TOPIC APPLIES TO: SQL Server (starting with 2016) Azure SQL Database Azure SQL Data
Warehouse Parallel Data Warehouse
Disk space is an important consideration when you create, rebuild, or drop indexes. Inadequate disk space can
degrade performance or even cause the index operation to fail. This topic provides general information that can
help you determine the amount of disk space required for index data definition language (DDL) operations.
NOTE
The SORT_IN_TEMPDB option cannot be set for DROP INDEX statements. The temporary mapping index is always created
in the same filegroup or partition scheme as the target index.
Online index operations use row versioning to isolate the index operation from the effects of modifications made
by other transactions. This avoids the need for requesting share locks on rows that have been read. Concurrent
user update and delete operations during online index operations require space for version records in tempdb.
For more information, see Perform Index Operations Online .
Related Tasks
Index Disk Space Example
Transaction Log Disk Space for Index Operations
Estimate the Size of a Table
Estimate the Size of a Clustered Index
Estimate the Size of a Nonclustered Index
Estimate the Size of a Heap
Related Content
CREATE INDEX (Transact-SQL)
ALTER INDEX (Transact-SQL)
DROP INDEX (Transact-SQL)
Specify Fill Factor for an Index
Reorganize and Rebuild Indexes
Transaction Log Disk Space for Index Operations
3/24/2017 • 1 min to read • Edit Online
Large-scale index operations can generate large data loads that can cause the transaction log to fill quickly. To
make sure that the index operation can be rolled back, the transaction log cannot be truncated until the index
operation has completed; however, the log can be backed up during the index operation. Therefore, the transaction
log must have sufficient room to store both the index operation transactions and any concurrent user transactions
for the duration of the index operation. This is true for both offline and online index operations. Because the
underlying tables cannot be accessed during an offline index operation, there may be few user transactions and the
log may not grow as quickly. Online index operations do not prevent concurrent user activity, therefore, large-scale
online index operations combined with significant concurrent user transactions can cause continuous growth of
the transaction log without an option to truncate the log.
Recommendations
When you run large-scale index operations, consider the following recommendations:
1. Make sure the transaction log has been backed up and truncated before running large-scale index
operations online, and that the log has sufficient space to store the projected index and user transactions.
2. Consider setting the SORT_IN_TEMPDB option to ON for the index operation. This separates the index
transactions from the concurrent user transactions. The index transactions will be stored in the tempdb
transaction log, and the concurrent user transactions will be stored in the transaction log of the user
database. This allows for the transaction log of the user database to be truncated during the index operation
if it is required. Additionally, if the tempdb log is not on the same disk as the user database log, the two
logs are not competing for the same disk space.
NOTE
Verify that the tempdb database and transaction log have sufficient disk space to handle the index operation. The
tempdb transaction log cannot be truncated until the index operation is completed.
3. Use a database recovery model that allows for minimal logging of the index operation. This may reduce the
size of the log and prevent the log from filling the log space.
4. Do not run the online index operation in an explicit transaction. The log will not be truncated until the
explicit transaction ends.
Related Content
Disk Space Requirements for Index DDL Operations
Index Disk Space Example
Index Disk Space Example
3/24/2017 • 5 min to read • Edit Online
Whenever an index is created, rebuilt, or dropped, disk space for both the old (source) and new (target) structures
is required in their appropriate files and filegroups. The old structure is not deallocated until the index creation
transaction commits. Additional temporary disk space for sorting operations may also be needed. For more
information, see Disk Space Requirements for Index DDL Operations.
In this example, disk space requirements to create a clustered index are determined.
Assume the following conditions are true before creating the clustered index:
The existing table (heap) contains 1 million rows. Each row is 200 bytes long.
Nonclustered index A contains 1 million rows. Each row is 50 bytes long.
Nonclustered index B contains 1 million rows. Each row is 80 bytes long.
The index create memory option is set to 2 MB.
A fill factor value of 80 is used for all existing and new indexes. This means the pages are 80 percent full.
NOTE
As a result of creating a clustered index, the two nonclustered indexes must be rebuilt to replace the row indicator
with the new clustered index key.
Offline index operation with SORT_IN_TEMPDB = ON Total space during the operation: 1018 MB
-
tempdb: 202 MB*
Offline index operation with SORT_IN_TEMPDB = OFF Total space during the operation: 816 MB
Online index operation with SORT_IN_TEMPDB = ON Total space during the operation: 1058 MB
-
tempdb (includes mapping index): 242 MB*
Online index operation with SORT_IN_TEMPDB = OFF Total space during the operation: 856 MB
Related Content
Disk Space Requirements for Index DDL Operations
Transaction Log Disk Space for Index Operations
Reorganize and Rebuild Indexes
4/6/2017 • 9 min to read • Edit Online
THIS TOPIC APPLIES TO: SQL Server (starting with 2008) Azure SQL Database Azure SQL Data
Warehouse Parallel Data Warehouse
This topic describes how to reorganize or rebuild a fragmented index in SQL Server 2016 by using SQL Server
Management Studio or Transact-SQL. The SQL Server Database Engine automatically maintains indexes whenever
insert, update, or delete operations are made to the underlying data. Over time these modifications can cause the
information in the index to become scattered in the database (fragmented). Fragmentation exists when indexes
have pages in which the logical ordering, based on the key value, does not match the physical ordering inside the
data file. Heavily fragmented indexes can degrade query performance and cause your application to respond
slowly.
You can remedy index fragmentation by reorganizing or rebuilding an index. For partitioned indexes built on a
partition scheme, you can use either of these methods on a complete index or a single partition of an index.
Rebuilding an index drops and re-creates the index. This removes fragmentation, reclaims disk space by
compacting the pages based on the specified or existing fill factor setting, and reorders the index rows in
contiguous pages. When ALL is specified, all indexes on the table are dropped and rebuilt in a single transaction.
Reorganizing an index uses minimal system resources. It defragments the leaf level of clustered and nonclustered
indexes on tables and views by physically reordering the leaf-level pages to match the logical, left to right, order of
the leaf nodes. Reorganizing also compacts the index pages. Compaction is based on the existing fill factor value.
In This Topic
Before you begin:
Detecting Fragmentation
Limitations and Restrictions
Security
To check the fragmentation of an index, using:
SQL Server Management Studio
Transact-SQL
To reorganize or rebuild an index, using:
SQL Server Management Studio
Transact-SQL
After the degree of fragmentation is known, use the following table to determine the best method to correct the
fragmentation.
* Rebuilding an index can be executed online or offline. Reorganizing an index is always executed online. To
achieve availability similar to the reorganize option, you should rebuild indexes online.
These values provide a rough guideline for determining the point at which you should switch between ALTER
INDEX REORGANIZE and ALTER INDEX REBUILD. However, the actual values may vary from case to case. It is
important that you experiment to determine the best threshold for your environment. Very low levels of
fragmentation (less than 5 percent) should not be addressed by either of these commands because the benefit
from removing such a small amount of fragmentation is almost always vastly outweighed by the cost of
reorganizing or rebuilding the index.
NOTE
In general, fragmentation on small indexes is often not controllable. The pages of small indexes are sometimes stored on
mixed extents. Mixed extents are shared by up to eight objects, so the fragmentation in a small index might not be reduced
after reorganizing or rebuilding the index.
Security
Permissions
Requires ALTER permission on the table or view. User must be a member of the sysadmin fixed server role or the
db_ddladmin and db_owner fixed database roles.
Using Transact-SQL
To check the fragmentation of an index
1. In Object Explorer, connect to an instance of Database Engine.
2. On the Standard bar, click New Query.
3. Copy and paste the following example into the query window and click Execute.
USE AdventureWorks2012;
GO
-- Find the average fragmentation percentage of all indexes
-- in the HumanResources.Employee table.
SELECT a.index_id, name, avg_fragmentation_in_percent
FROM sys.dm_db_index_physical_stats (DB_ID(N'AdventureWorks2012'),
OBJECT_ID(N'HumanResources.Employee'), NULL, NULL, NULL) AS a
JOIN sys.indexes AS b ON a.object_id = b.object_id AND a.index_id = b.index_id;
GO
The statement above might return a result set similar to the following.
(6 row(s) affected)
Using Transact-SQL
To reorganize a defragmented index
1. In Object Explorer, connect to an instance of Database Engine.
2. On the Standard bar, click New Query.
3. Copy and paste the following example into the query window and click Execute.
USE AdventureWorks2012;
GO
-- Reorganize the IX_Employee_OrganizationalLevel_OrganizationalNode index on the
HumanResources.Employee table.
USE AdventureWorks2012;
GO
-- Reorganize all indexes on the HumanResources.Employee table.
ALTER INDEX ALL ON HumanResources.Employee
REORGANIZE ;
GO
USE AdventureWorks2012;
GO
ALTER INDEX PK_Employee_BusinessEntityID ON HumanResources.Employee
REBUILD;
GO
USE AdventureWorks2012;
GO
ALTER INDEX ALL ON Production.Product
REBUILD WITH (FILLFACTOR = 80, SORT_IN_TEMPDB = ON,
STATISTICS_NORECOMPUTE = ON);
GO
See Also
Microsoft SQL Server 2000 Index Defragmentation Best Practices
Specify Fill Factor for an Index
3/24/2017 • 4 min to read • Edit Online
THIS TOPIC APPLIES TO: SQL Server (starting with 2016) Azure SQL Database Azure SQL Data
Warehouse Parallel Data Warehouse
This topic describes what fill factor is and how to specify a fill factor value on an index in SQL Server 2016 by using
SQL Server Management Studio or Transact-SQL.
The fill-factor option is provided for fine-tuning index data storage and performance. When an index is created or
rebuilt, the fill-factor value determines the percentage of space on each leaf-level page to be filled with data,
reserving the remainder on each page as free space for future growth. For example, specifying a fill-factor value of
80 means that 20 percent of each leaf-level page will be left empty, providing space for index expansion as data is
added to the underlying table. The empty space is reserved between the index rows rather than at the end of the
index.
The fill-factor value is a percentage from 1 to 100, and the server-wide default is 0 which means that the leaf-level
pages are filled to capacity.
NOTE
Fill-factor values 0 and 100 are the same in all respects.
In This Topic
Before you begin:
Performance Considerations
Security
To specify a fill factor in an index, using:
SQL Server Management Studio
Transact-SQL
Using Transact-SQL
To specify a fill factor in an existing index
1. In Object Explorer, connect to an instance of Database Engine.
2. On the Standard bar, click New Query.
3. Copy and paste the following example into the query window and click Execute. The example rebuilds an
existing index and applies the specified fill factor during the rebuild operation.
USE AdventureWorks2012;
GO
-- Rebuilds the IX_Employee_OrganizationLevel_OrganizationNode index
-- with a fill factor of 80 on the HumanResources.Employee table.
USE AdventureWorks2012;
GO
/* Drops and re-creates the IX_Employee_OrganizationLevel_OrganizationNode index on the
HumanResources.Employee table with a fill factor of 80.
*/
THIS TOPIC APPLIES TO: SQL Server (starting with 2016) Azure SQL Database Azure SQL Data
Warehouse Parallel Data Warehouse
This topic describes how to create, rebuild, or drop indexes online in SQL Server 2016 by using SQL Server
Management Studio or Transact-SQL. The ONLINE option allows concurrent user access to the underlying table
or clustered index data and any associated nonclustered indexes during these index operations. For example,
while a clustered index is being rebuilt by one user, that user and others can continue to update and query the
underlying data. When you perform data definition language (DDL) operations offline, such as building or
rebuilding a clustered index; these operations hold exclusive locks on the underlying data and associated indexes.
This prevents modifications and queries to the underlying data until the index operation is complete.
NOTE
Online index operations are not available in every SQL Server edition. For more information, see Features Supported by the
Editions of SQL Server 2016.
In This Topic
Before you begin:
Limitations and Restrictions
Security
To rebuild an index online, using:
SQL Server Management Studio
Transact-SQL
Using Transact-SQL
To create, rebuild, or drop an index online
1. In Object Explorer, connect to an instance of Database Engine.
2. On the Standard bar, click New Query.
3. Copy and paste the following example into the query window and click Execute. The example rebuilds an
existing online
USE AdventureWorks2012;
GO
ALTER INDEX AK_Employee_NationalIDNumber ON HumanResources.Employee
REBUILD WITH (ONLINE = ON);
GO
The following example deletes a clustered index online and moves the resulting table (heap) to the
filegroup NewGroup by using the MOVE TO clause. The sys.indexes , sys.tables , and sys.filegroups
catalog views are queried to verify the index and table placement in the filegroups before and after the
move.
USE AdventureWorks2012;
GO
--Create a clustered index on the PRIMARY filegroup if the index does not exist.
IF NOT EXISTS (SELECT name FROM sys.indexes WHERE name =
N'AK_BillOfMaterials_ProductAssemblyID_ComponentID_StartDate')
CREATE UNIQUE CLUSTERED INDEX
AK_BillOfMaterials_ProductAssemblyID_ComponentID_StartDate
ON Production.BillOfMaterials (ProductAssemblyID, ComponentID,
StartDate)
ON 'PRIMARY';
GO
-- Verify filegroup location of the clustered index.
SELECT t.name AS [Table Name], i.name AS [Index Name], i.type_desc,
i.data_space_id, f.name AS [Filegroup Name]
FROM sys.indexes AS i
JOIN sys.filegroups AS f ON i.data_space_id = f.data_space_id
JOIN sys.tables as t ON i.object_id = t.object_id
AND i.object_id = OBJECT_ID(N'Production.BillOfMaterials','U')
GO
--Create filegroup NewGroup if it does not exist.
IF NOT EXISTS (SELECT name FROM sys.filegroups
WHERE name = N'NewGroup')
BEGIN
ALTER DATABASE AdventureWorks2012
ADD FILEGROUP NewGroup;
ALTER DATABASE AdventureWorks2012
ADD FILE (NAME = File1,
FILENAME = 'C:\Program Files\Microsoft SQL
Server\MSSQL10.MSSQLSERVER\MSSQL\DATA\File1.ndf')
TO FILEGROUP NewGroup;
END
GO
--Verify new filegroup
SELECT * from sys.filegroups;
GO
-- Drop the clustered index and move the BillOfMaterials table to
-- the Newgroup filegroup.
-- Set ONLINE = OFF to execute this example on editions other than Enterprise Edition.
DROP INDEX AK_BillOfMaterials_ProductAssemblyID_ComponentID_StartDate
ON Production.BillOfMaterials
WITH (ONLINE = ON, MOVE TO NewGroup);
GO
-- Verify filegroup location of the moved table.
SELECT t.name AS [Table Name], i.name AS [Index Name], i.type_desc,
i.data_space_id, f.name AS [Filegroup Name]
FROM sys.indexes AS i
JOIN sys.filegroups AS f ON i.data_space_id = f.data_space_id
JOIN sys.tables as t ON i.object_id = t.object_id
AND i.object_id = OBJECT_ID(N'Production.BillOfMaterials','U');
GO
THIS TOPIC APPLIES TO: SQL Server (starting with 2016) Azure SQL Database Azure SQL Data
Warehouse Parallel Data Warehouse
This topic defines the structures that exist during an online index operation and shows the activities associated with
these structures.
* The index operation will wait for any uncommitted update transactions to complete before acquiring the S lock or
SCH-M lock on the table.
** The resource lock INDEX_BUILD_INTERNAL_RESOURCE prevents the execution of concurrent data definition
language (DDL) operations on the source and preexisting structures while the index operation is in progress. For
example, this lock prevents concurrent rebuild of two indexes on the same table. Although this resource lock is
associated with the Sch-M lock, it does not prevent data manipulation statements.
The previous table shows a single Shared (S) lock acquired during the build phase of an online index operation that
involves a single index. When clustered and nonclustered indexes are built, or rebuilt, in a single online index
operation (for example, during the initial clustered index creation on a table that contains one or more nonclustered
indexes) two short-term S locks are acquired during the build phase followed by long-term Intent Shared (IS) locks.
One S lock is acquired first for the clustered index creation and when creating the clustered index is completed, a
second short-term S lock is acquired for creating the nonclustered indexes. After the nonclustered indexes are
created, the S lock is downgraded to an IS lock until the final phase of the online index operation.
Target Structure Activities
The following table lists the activities that involve the target structure during each phase of the index operation and
the corresponding locking strategy.
SCH-M
The target is not accessed by SELECT statements issued by the user until the index operation is completed.
After the preparation and final phase is completed, the query and update plans that are stored in the procedure
cache are invalidated. Subsequent queries will use the new index.
The lifetime of a cursor declared on a table that is involved in an online index operation is limited by the online
index phases. Update cursors are invalidated at each phase. Read-only cursors are invalidated only after the final
phase.
Related Content
Perform Index Operations Online
Guidelines for Online Index Operations
Guidelines for Online Index Operations
4/29/2017 • 7 min to read • Edit Online
THIS TOPIC APPLIES TO: SQL Server (starting with 2008) Azure SQL Database Azure SQL Data
Warehouse Parallel Data Warehouse
When you perform online index operations, the following guidelines apply:
Clustered indexes must be created, rebuilt, or dropped offline when the underlying table contains the
following large object (LOB) data types: image, ntext, and text.
Nonunique nonclustered indexes can be created online when the table contains LOB data types but none of
these columns are used in the index definition as either key or nonkey (included) columns.
Indexes on local temp tables cannot be created, rebuilt, or dropped online. This restriction does not apply to
indexes on global temp tables.
Indexes can be resumed from where it stopped after an unexpected failure, database failover, or a PAUSE
command. See Alter Index. This feature is in public preview for SQL Server 2017.
NOTE
Online index operations are not available in every edition of Microsoft SQL Server. For a list of features that are supported by
the editions of SQL Server, see Features supported by editions.
The following table shows the index operations that can be performed online, the indexes that are excluded from
these online operations, and resumable index restrictions. Additional restrictions are also included.
ALTER INDEX REBUILD Disabled clustered index or disabled Specifying the keyword ALL may cause
indexed view the operation to fail when the table
contains an excluded index.
XML index
Additional restrictions on rebuilding
Columnstore index disabled indexes apply. For more
information, see Disable Indexes and
Index on a local temp table Constraints.
XML index
ONLINE INDEX OPERATION EXCLUDED INDEXES OTHER RESTRICTIONS
Nonclustered index
ALTER TABLE ADD CONSTRAINT Index on a local temp table Only one subclause is allowed at a time.
(PRIMARY KEY or UNIQUE) For example, you cannot add and drop
Clustered index PRIMARY KEY or UNIQUE constraints in
the same ALTER TABLE statement.
The underlying table cannot be modified, truncated, or dropped while an online index operation is in process.
The online option setting (ON or OFF) specified when you create or drop a clustered index is applied to any
nonclustered indexes that must be rebuilt. For example, if the clustered index is built online by using CREATE
INDEX WITH DROP_EXISTING, ONLINE=ON, all associated nonclustered indexes are re-created online also.
When you create or rebuild a UNIQUE index online, the index builder and a concurrent user transaction may try to
insert the same key, therefore violating uniqueness. If a row entered by a user is inserted into the new index
(target) before the original row from the source table is moved to the new index, the online index operation will
fail.
Although not common, the online index operation can cause a deadlock when it interacts with database updates
because of user or application activities. In these rare cases, the SQL Server Database Engine will select the user or
application activity as a deadlock victim.
You can perform concurrent online index DDL operations on the same table or view only when you are creating
multiple new nonclustered indexes, or reorganizing nonclustered indexes. All other online index operations
performed at the same time fail. For example, you cannot create a new index online while rebuilding an existing
index online on the same table.
An online operation cannot be performed when an index contains a column of the large object type, and in the
same transaction there are update operations before this online operation. To work around this issue, place the
online operation outside the transaction or place it before any updates in the transaction.
Performance Considerations
Although online index operations permit concurrent user update activity, the index operations will take longer if
the update activity is very heavy. Typically, online index operations will be slower than equivalent offline index
operations regardless of the concurrent update activity level.
Because both the source and target structures are maintained during the online index operation, the resource
usage for insert, update, and delete transactions is increased, potentially up to double. This could cause a decrease
in performance and greater resource usage, especially CPU time, during the index operation. Online index
operations are fully logged.
Although we recommend online operations, you should evaluate your environment and specific requirements. It
may be optimal to run index operations offline. In doing this, users have restricted access to the data during the
operation, but the operation finishes faster and uses fewer resources.
On multiprocessor computers that are running SQL Server 2016, index statements may use more processors to
perform the scan and sort operations associated with the index statement just like other queries do. You can use
the MAXDOP index option to control the number of processors dedicated to the online index operation. In this way,
you can balance the resources that are used by index operation with those of the concurrent users. For more
information, see Configure Parallel Index Operations. For more information about the editions of SQL Server that
support Parallel indexed operations, see Features Supported by editions.
Because an S-lock or Sch-M lock is held in the final phase of the index operation, be careful when you run an
online index operation inside an explicit user transaction, such as BEGIN TRANSACTION...COMMIT block. Doing this
causes the lock to be held until the end of the transaction, therefore impeding user concurrency.
Online index rebuilding may increase fragmentation when it is allowed to run with MAX DOP > 1 and
ALLOW_PAGE_LOCKS = OFF options. For more information, see How It Works: Online Index Rebuild - Can Cause
Increased Fragmentation.
When you perform resumable online index rebuild the following guidelines apply:
Managing, planning and extending of index maintenance windows. You can pause and restart an index rebuild
operation multiple times to fit your maintenance windows.
Recovering from index rebuild failures (such as database failovers or running out of disk space).
When an index operation is paused, both the original index and the the newly created one require disk
space and need to be updated during DML operations.
Enables truncation of truncation logs during an index rebuild operation (this operation cannot be performed
for a regular online index operation).
SORT_IN_TEMPDB=ON option is not supported
IMPORTANT
Resumable rebuild does not require you to keep open a long running truncation, allowing log truncation during this
operation and a better log space management. With the new design, we managed to keep necessary data in a database
together with all references required to restart the resumable operation.
Generally, there is no performance difference between resumable and non-resumable online index rebuild. When
you update a resumable index while an index rebuild operation is paused:
For read-mostly workloads, the performance impact is insignificant.
For update-heavy workloads, you may experience some throughput degradation (our testing shows less than
10% degradation).
Generally, there is no difference in defragmentation quality between resumable and non-resumable online index
rebuild.
Related Content
How Online Index Operations Work
Perform Index Operations Online
ALTER INDEX (Transact-SQL)
CREATE INDEX (Transact-SQL)
Configure Parallel Index Operations
3/24/2017 • 4 min to read • Edit Online
THIS TOPIC APPLIES TO: SQL Server (starting with 2016) Azure SQL Database Azure SQL Data
Warehouse Parallel Data Warehouse
This topic defines max degree of parallelism and explains how to modify this setting in SQL Server 2016 by using
SQL Server Management Studio or Transact-SQL. On multiprocessor computers that are running SQL Server
Enterprise or higher, index statements may use multiple processors to perform the scan, sort, and index operations
associated with the index statement just like other queries do. The number of processors used to run a single index
statement is determined by the max degree of parallelism configuration option, the current workload, and the
index statistics. The max degree of parallelism option determines the maximum number of processors to use in
parallel plan execution. If the SQL Server Database Engine detects that the system is busy, the degree of parallelism
of the index operation is automatically reduced before statement execution starts. The Database Engine can also
reduce the degree of parallelism if the leading key column of a non-partitioned index has a limited number of
distinct values or the frequency of each distinct value varies significantly.
NOTE
Parallel index operations are not available in every SQL Server edition. For more information, see Features Supported by the
Editions of SQL Server 2016
In This Topic
Before you begin:
Limitations and Restrictions
Security
To set the max degree of parallelism, using:
SQL Server Management Studio
Transact-SQL
VALUE DESCRIPTION
VALUE DESCRIPTION
Parallel index execution and the MAXDOP index option apply to the following Transact-SQL statements:
CREATE INDEX
ALTER INDEX REBUILD
DROP INDEX (This applies to clustered indexes only.)
ALTER TABLE ADD (index) CONSTRAINT
ALTER TABLE DROP (clustered index) CONSTRAINT
The MAXDOP index option cannot be specified in the ALTER INDEX REORGANIZE statement.
Memory requirements for partitioned index operations that require sorting can be greater if the query
optimizer applies degrees of parallelism to the build operation. The higher the degrees of parallelism, the
greater the memory requirement is. For more information, see Partitioned Tables and Indexes.
Security
Permissions
Requires ALTER permission on the table or view.
Using Transact-SQL
To set max degree of parallelism on an existing index
1. In Object Explorer, connect to an instance of Database Engine.
2. On the Standard bar, click New Query.
3. Copy and paste the following example into the query window and click Execute.
USE AdventureWorks2012;
GO
/*Alters the IX_ProductVendor_VendorID index on the Purchasing.ProductVendor table so that, if the
server has eight or more processors, the Database Engine will limit the execution of the index
operation to eight or fewer processors.
*/
ALTER INDEX IX_ProductVendor_VendorID ON Purchasing.ProductVendor
REBUILD WITH (MAXDOP=8);
GO
USE AdventureWorks2012;
GO
CREATE INDEX IX_ProductVendor_NewVendorID
ON Purchasing.ProductVendor (BusinessEntityID)
WITH (MAXDOP=8);
GO
THIS TOPIC APPLIES TO: SQL Server (starting with 2016) Azure SQL Database Azure SQL Data
Warehouse Parallel Data Warehouse
The sections in this topic refer to various index properties that are available by using SQL Server Management
Studio dialogs.
In This Topic:
Index Properties General Page
Select (Index) Columns Dialog Box
Index Properties Storage Page
Index Properties Spatial Page
Index Properties Filter Page
NOTE
If the index type is Primary XML or Spatial, this column does not appear in the table.
Data Type
Displays the data type information.
NOTE
If the table column is a computed column, Data type displays "computed column."
Size
Displays the maximum number of bytes required to store the column data type. Displays zero (0) for a spatial or
XML column.
Identity
Displays whether the column participating in the index key is an identity column.
Allow NULLs
Displays whether the column participating in the index key allows NULL values to be stored in the table or view
column.
Add
Adds a column to the index key. Select table columns from the Select Columns from <table name> dialog box
that appears when you click Add. For a spatial index, after you select one column, this button is dimmed.
Remove
Removes the selected column from participation in the index key.
Move Up
Moves the selected column up in the index key grid.
Move Down
Moves the selected column down in the index key grid.
Columnstore columns
Click Add to select columns for the columnstore index. For limitations on a columnstore index, see CREATE
COLUMNSTORE INDEX (Transact-SQL).
Included columns
Include nonkey columns in the nonclustered index. This option allows you to bypass the current index limits on the
total size of an index key and the maximum number of columns participating in an index key by adding columns as
nonkey columns in the leaf level of the nonclustered index. For more information, see Create Indexes with Included
Columns
Select (Index) Columns Dialog Box
Use this page to add columns to the Index Properties General page when creating or modifying an index.
Check box
Select to add columns.
Name
Name of the column.
Data Type
The data type of the column.
Bytes
The size of the column in bytes.
Identity
Displays Yes for identity columns, and No when the column is not an identity column.
Allow Nulls
Displays Yes when the table definition allows null values for the column. Displays No when the table definition
does not allow nulls for the column.
NOTE
This option is not available for XML indexes, or if the index is a disabled clustered index.
NOTE
If a value greater than the number of available CPUs is specified, the actual number of available CPUs is used.
Filter Page
Use this page to enter the filter predicate for a filtered index. For more information, see Create Filtered Indexes.
Filter Expression
Defines which data rows to include in the filtered index. For example,
StartDate > '20000101' AND EndDate IS NOT NULL'.
See Also
Set Index Options
INDEXPROPERTY (Transact-SQL)
sys.indexes (Transact-SQL)
Columnstore indexes - overview
3/24/2017 • 9 min to read • Edit Online
THIS TOPIC APPLIES TO: SQL Server (starting with 2012) Azure SQL Database Azure SQL Data
Warehouse Parallel Data Warehouse
The columnstore index is the standard for storing and querying large data warehousing fact tables. It uses column-
based data storage and query processing to achieve up to 10x query performance gains in your data warehouse
over traditional row-oriented storage, and up to 10x data compression over the uncompressed data size.
Beginning with SQL Server 2016, columnstore indexes enable operational analytics, the ability to run performant
real-time analytics on a transactional workload.
Jump to scenarios:
Columnstore Indexes for Data Warehousing
Get started with Columnstore for real time operational analytics
NOTE
In discussions about columnstore indexes, we use the terms rowstore and columnstore to emphasize the format for the data
storage.
rowgroup
A row group is a group of rows that are compressed into columnstore format at the same time. A rowgroup
usually contains the maximum number of rows per rowgroup which is 1,048,576 rows.
For high performance and high compression rates, the columnstore index slices the table into groups of rows,
called rowgroups, and then compresses each rowgroup in a column-wise manner. The number of rows in the
rowgroup must be large enough to improve compression rates, and small enough to benefit from in-memory
operations.
column segment
A column segment is a column of data from within the rowgroup.
Each rowgroup contains one column segment for every column in the table.
Each column segment is compressed together and stored on physical media.
To reduce fragmentation of the column segments and improve performance, the columnstore index might
store some data temporarily into a clustered index, which is called a deltastore, and a btree list of IDs for
deleted rows. The deltastore operations are handled behind the scenes. To return the correct query results,
the clustered columnstore index combines query results from both the columnstore and the deltastore.
deltastore
Used with clustered column store indexes only, a deltastore is a clustered index that improves columnstore
compression and performance by storing rows until the number of rows reaches a threshold and are then
moved into the columnstore.
During a large bulk load, most of the rows go directly to the columnstore without passing through the
deltastore. Some rows at the end of the bulk load might be too few in number to meet the minimum size of
a rowgroup which is 102,400 rows. When this happens, the final rows go to the deltastore instead of the
columnstore. For small bulk loads with less than 102,400 rows, all of the rows go directly to the deltastore.
When the deltastore reaches the maximum number of rows, it becomes closed. A tuple-mover process
checks for closed row groups. When it finds the closed rowgroup, it compresses it and stores it into the
columnstore.
nonclustered columnstore index
A nonclustered columnstore index and a clustered columnstore index function the same. The difference is a
nonclustered index is a secondary index created on a rowstore table, whereas a clustered columnstore index
is the primary storage for the entire table.
The nonclustered index contains a copy of part or all of the rows and columns in the underlying table. The
index is defined as one or more columns of the table, and has an optional condition that filters the rows.
A nonclustered columnstore index enables real-time operational analytics in which the OLTP workload uses
the underlying clustered index, while analytics run concurrently on the columnstore index. For more
information, see Get started with Columnstore for real time operational analytics.
batch execution
Batch execution is a query processing method in which queries process multiple rows together. Queries on
columnstore indexes use batch mode execution which improves query performance typically 2-4x. Batch
execution is closely integrated with, and optimized around, the columnstore storage format. Batch-mode
execution is sometimes known as vector-based or vectorized execution.
Metadata
All of the columns in a columnstore index are stored in the metadata as included columns. The columnstore index
does not have key columns.
sys.indexes (Transact-SQL)
sys.index_columns (Transact-SQL)
sys.partitions (Transact-SQL)
sys.internal_partitions (Transact-SQL)
sys.column_store_segments (Transact-SQL)
sys.column_store_dictionaries (Transact-SQL)
sys.column_store_row_groups (Transact-SQL)
sys.dm_db_column_store_row_group_operational_stats (Transact-SQL)
sys.dm_db_column_store_row_group_physical_stats (Transact-SQL)
sys.dm_column_store_object_pool (Transact-SQL)
sys.dm_db_column_store_row_group_operational_stats (Transact-SQL)
sys.dm_db_index_operational_stats (Transact-SQL)
sys.dm_db_index_physical_stats (Transact-SQL)
Related Tasks
All relational tables, unless you specify them as a clustered columnstore index, use rowstore as the underlying data
format. CREATE TABLE creates a rowstore table unless you specify the WITH CLUSTERED COLUMNSTORE INDEX
option.
When you create a table with the CREATE TABLE statement you can create the table as a columnstore by specifying
the WITH CLUSTERED COLUMNSTORE INDEX option. If you already have a rowstore table and want to convert it
to a columnstore, you can use the CREATE COLUMNSTORE INDEX statement. For examples, see.
Create a table as a columnstore. CREATE TABLE (Transact-SQL) Beginning with SQL Server 2016, you
can create the table as a clustered
columnstore index. You do not have to
first create a rowstore table and then
convert it to columnstore.
TASK REFERENCE TOPICS NOTES
Create a memory table with a CREATE TABLE (Transact-SQL) Beginning with SQL Server 2016, you
columnstore index. can create a memory-optimized table
with a columnstore index. The
columnstore index can also be added
after the table is created, using the
ALTER TABLE ADD INDEX syntax.
Convert a rowstore table to a CREATE COLUMNSTORE INDEX Convert an existing heap or binary tree
columnstore. (Transact-SQL) to a columnstore. Examples show how
to handle existing indexes and also the
name of the index when performing this
conversion.
Convert a columnstore table to a CREATE COLUMNSTORE INDEX Usually this is not necessary, but there
rowstore. (Transact-SQL) can be times when you need to perform
this conversion. Examples show how to
convert a columnstore to a heap or
clustered index.
Create a columnstore index on a CREATE COLUMNSTORE INDEX A rowstore table can have one
rowstore table. (Transact-SQL) columnstore index. Beginning with SQL
Server 2016, the columnstore index can
have a filtered condition. Examples
show the basic syntax.
Create performant indexes for Get started with Columnstore for real Describes how to create complementary
operational analytics. time operational analytics columnstore and btree indexes so that
OLTP queries use btree indexes and
analytics queries use columnstore
indexes.
Create performant columnstore indexes Columnstore Indexes for Data Describes how to use btree indexes on
for data warehousing. Warehousing columnstore tables to create
performant data warehousing queries.
Use a btree index to enforce a primary Columnstore Indexes for Data Shows how to combine btree and
key constraint on a columnstore index. Warehousing columnstore indexes to enforce primary
key constraints on the columnstore
index.
Drop a columnstore index DROP INDEX (Transact-SQL) Dropping a columnstore index uses the
standard DROP INDEX syntax that
btree indexes use. Dropping a clustered
columnstore index will convert the
columnstore table to a heap.
Delete a row from a columnstore index DELETE (Transact-SQL) Use DELETE (Transact-SQL) to delete a
row.
Update a row in the columnstore index UPDATE (Transact-SQL) Use UPDATE (Transact-SQL) to update
a row.
Force all rows in the deltastore to go ALTER INDEX (Transact-SQL) ... ALTER INDEX with the REBUILD option
into the columnstore. REBUILD forces all rows to go into the
columnstore.
Columnstore Indexes Defragmentation
See Also
Columnstore Indexes Data Loading
Columnstore Indexes Versioned Feature Summary
Columnstore Indexes Query Performance
Get started with Columnstore for real time operational analytics
Columnstore Indexes for Data Warehousing
Columnstore Indexes Defragmentation
Columnstore indexes - architecture
3/24/2017 • 6 min to read • Edit Online
THIS TOPIC APPLIES TO: SQL Server (starting with 2016) Azure SQL Database Azure SQL Data
Warehouse Parallel Data Warehouse
Learn how a columnstore index is architected. Knowing these basics will make it easier to understand other
columnstore articles that explain how to use them effectively.
A columnstore is data that is logically organized as a table with rows and columns, and physically stored in a
column-wise data format.
A columnstore index physically stores most of the data in columnstore format. In columnstore format, the data is
compressed and uncompressed as columns. There is no need to uncompress other values in each row that are not
requested by the query. This makes it fast to scan an entire column of a large table.
A rowstore is data that is logically organized as a table with rows and columns, and then physically stored in a
row-wise data format. This has been the traditional way to store relational table data such as a heap or clustered
"btree" index.
A columnstore index also physically stores some rows in a rowstore format called a deltastore. The deltastore,also
called delta rowgroups, is a holding place for rows that are too few in number to qualify for compression into the
columnstore. Each delta rowgroup is implemented as a clustered btree index.
A deltastore is a a holding place for rows that are too few in number to be compressed into the columnstore.
The deltastore is a rowstore.
When delta rowgroups are full they get compressed into the
columnstore
Clustered columnstore indexes collect up to 1,048,576 rows in each delta rowgroup before compressing the
rowgroup into the columnstore. This improves the compression of the columnstore index. When a delta rowgroup
contains 1,048,576 rows, the columnstore index marks the rowgroup as closed. A background process, called the
tuple-mover, finds each closed rowgroup and compresses it into the columnstore.
You can force delta rowgroups into the columnstore by using ALTER INDEX to rebuild or reorganize the index. Note
that if there is memory pressure during compression, the columnstore index might reduce the number of rows in
the compressed rowgroup.
Each table partition has its own rowgroups and delta rowgroups
The concept of partitioning is the same in both a clustered index, a heap, and a columnstore index. Partitioning a
table divides the table into smaller groups of rows according to a range of column values. It is often used for
managing the data. For example, you could create a partition for each year of data, and then use partition switching
to archive data to less expensive storage. Partition switching works on columnstore indexes and makes it easy to
move a partition of data to another location.
Rowgroups are always defined within a table partition. When a columnstore index is partitioned, each partition has
its own compressed rowgroups and delta rowgroups.
Each partition can have multiple delta rowgroups
Each partition can have more than one delta rowgroups. When the columnstore index needs to add data to a delta
rowgroup and the delta rowgroup is locked, the columnstore index will try to obtain a lock on a different delta
rowgroup. If there are no delta rowgroups available, the columnstore index will create a new delta rowgroup. For
example, a table with 10 partitions could easily have 20 or more delta rowgroups.
You can combine columnstore and rowstore indexes on the same table
A nonclustered index contains a copy of part or all of the rows and columns in the underlying table. The index is
defined as one or more columns of the table, and has an optional condition that filters the rows.
Beginning with SQL Server 2016, you can create an updatable nonclustered columnstore index on a rowstore table.
The columnstore index stores a copy of the data so you do need extra storage. However, the data in the
columnstore index will compress to a smaller size than the rowstore table requires. By doing this, you can run
analytics on the columnstore index and transactions on the rowstore index at the same time. The column store is
updated when data changes in the rowstore table, so both indexes are working against the same data.
Beginning with SQL Server 2016, you can have one or more nonclustered rowstore indexes on a columnstore
index. By doing this, you can perform efficient table seeks on the underlying columnstore. Other options become
available too. For example, you can enforce a primary key constraint by using a UNIQUE constraint on the rowstore
table. Since an non-unique value will fail to insert into the rowstore table, SQL Server cannot insert the value into
the columnstore.
Metadata
Use these metadata views to see attributes of columnstore indexes. More architectural information is embedded in
some of these views.
Note, all of the columns in a columnstore index are stored in the metadata as included columns. The columnstore
index does not have key columns.
sys.indexes (Transact-SQL)
sys.index_columns (Transact-SQL)
sys.partitions (Transact-SQL)
sys.internal_partitions (Transact-SQL)
sys.column_store_segments (Transact-SQL)
sys.column_store_dictionaries (Transact-SQL)
sys.column_store_row_groups (Transact-SQL)
sys.dm_db_column_store_row_group_operational_stats (Transact-SQL)
sys.dm_db_column_store_row_group_physical_stats (Transact-SQL)
sys.dm_column_store_object_pool (Transact-SQL)
sys.dm_db_column_store_row_group_operational_stats (Transact-SQL)
sys.dm_db_index_operational_stats (Transact-SQL)
sys.dm_db_index_physical_stats (Transact-SQL)
|
Next steps
For guidance on designing your columnstore indexes, see Columnstore indexes - design guidance
Columnstore indexes - design guidance
3/24/2017 • 14 min to read • Edit Online
THIS TOPIC APPLIES TO: SQL Server (starting with 2016) Azure SQL Database Azure SQL Data
Warehouse Parallel Data Warehouse
High-level recommendations for designing columnstore indexes. A small number of good decision decisions helps
you achieve the high data compression and query performance that columnstore indexes are designed to provide.
Prerequisites
This article assumes you are familiar with columnstore architecture and terminology. For more information, see
Columnstore indexes - overview and Columnstore indexes - architecture.
Know your data requirements
Before designing a columnstore index, understand as much as possible about your data requirements. For example,
think through the answers to these questions:
How large is my table?
Do my queries mostly perform analytics that scan large ranges of values? Columnstore indexes are designed to
work well for large range scans rather than looking up specific values.
Does my workload perform lots of updates and deletes? Columnstore indexes work well when the data is stable.
Queries should be updating and deleting less than 10% of the rows.
Do I have fact and dimension tables for a data warehouse?
Do I need to perform analytics on a transactional workload? If this is the case, see the columnstore design
guidance for real-time operational analytics.
You might not need a columnstore index. Rowstore tables with heaps or clustered indexes perform best on queries
that seek into the data, searching for a particular value, or for queries on a small range of values. Use rowstore
indexes with transactional workloads since they tend to require mostly table seeks instead of large range table
scans.
Nonclustered btree indexes on a Use to: 10x on average plus some additional
clustered columnstore index 1) Enforce primary key and foreign key storage for the NCIs.
constraints on a clustered columnstore
index.
2) Speedup queries that search for
specific values or small ranges of values.
3) Speedup updates and deletes of
specific rows.
Nonclustered columnstore index on a Use for: NCCI is an additional index that requires
disk-based heap or btree index 1) An OLTP workload that has some 10% more storage on average.
analytics queries. You can drop btree
indexes created for analytics and replace
them with one nonclustered
columnstore index.
2) Many traditional OLTP workloads
that perform Extract Transform and
Load (ETL) operations to move data to
a separate data warehouse. You can
eliminate ETL and a separate data
warehouse by creating a nonclustered
columnstore index on some of the
OLTP tables.
Related Tasks
These are tasks for creating and maintaining columnstore indexes.
Create a table as a columnstore. CREATE TABLE (Transact-SQL) Beginning with SQL Server 2016, you
can create the table as a clustered
columnstore index. You do not have to
first create a rowstore table and then
convert it to columnstore.
TASK REFERENCE TOPICS NOTES
Create a memory table with a CREATE TABLE (Transact-SQL) Beginning with SQL Server 2016, you
columnstore index. can create a memory-optimized table
with a columnstore index. The
columnstore index can also be added
after the table is created, using the
ALTER TABLE ADD INDEX syntax.
Convert a rowstore table to a CREATE COLUMNSTORE INDEX Convert an existing heap or binary tree
columnstore. (Transact-SQL) to a columnstore. Examples show how
to handle existing indexes and also the
name of the index when performing this
conversion.
Convert a columnstore table to a CREATE COLUMNSTORE INDEX Usually this is not necessary, but there
rowstore. (Transact-SQL) can be times when you need to perform
this conversion. Examples show how to
convert a columnstore to a heap or
clustered index.
Create a columnstore index on a CREATE COLUMNSTORE INDEX A rowstore table can have one
rowstore table. (Transact-SQL) columnstore index. Beginning with SQL
Server 2016, the columnstore index can
have a filtered condition. Examples show
the basic syntax.
Create performant indexes for Get started with Columnstore for real Describes how to create complementary
operational analytics. time operational analytics columnstore and btree indexes so that
OLTP queries use btree indexes and
analytics queries use columnstore
indexes.
Create performant columnstore indexes Columnstore indexes - data Describes how to use btree indexes on
for data warehousing. Warehousing columnstore tables to create
performant data warehousing queries.
Use a btree index to enforce a primary Columnstore indexes - data Shows how to combine btree and
key constraint on a columnstore index. warehousing columnstore indexes to enforce primary
key constraints on the columnstore
index.
Drop a columnstore index DROP INDEX (Transact-SQL) Dropping a columnstore index uses the
standard DROP INDEX syntax that btree
indexes use. Dropping a clustered
columnstore index will convert the
columnstore table to a heap.
Delete a row from a columnstore index DELETE (Transact-SQL) Use DELETE (Transact-SQL) to delete a
row.
Update a row in the columnstore index UPDATE (Transact-SQL) Use UPDATE (Transact-SQL) to update
a row.
Force all rows in the deltastore to go ALTER INDEX (Transact-SQL) ... ALTER INDEX with the REBUILD option
into the columnstore. REBUILD forces all rows to go into the
columnstore.
Columnstore indexes - defragmentation
Next steps
To create an empty columnstore index for:
SQL Server, use CREATE TABLE (Transact-SQL)
SQL Database, use CREATE TABLE on Azure SQL Database
SQL Data Warehouse, use CREATE TABLE (Azure SQL Data Warehouse)
To convert an existing rowstore heap or btree index to a clustered columnstore index, or to create a nonclustered
columnstore index, use:
CREATE COLUMNSTORE INDEX (Transact-SQL)
Columnstore indexes - data loading guidance
4/6/2017 • 6 min to read • Edit Online
THIS TOPIC APPLIES TO: SQL Server (starting with 2012) Azure SQL Database Azure SQL Data
Warehouse Parallel Data Warehouse
Options and recommendations for loading data into a columnstore index by using the standard SQL bulk loading
and trickle insert methods. Loading data into a columnstore index is an essential part of any data warehousing
process because it moves data into the index in preparation for analytics.
New to columnstore indexes? See Columnstore indexes - overview and Columnstore indexes - architecture.
![Note] On a rowstore table with a nonclustered columnstore index data, SQL Server always inserts data into
the base table. The data is never inserted directly into the columnstore index.
102,000 0 102,000
145,000 145,000 0
1,048,577 1,048,576 1
2,252,152 2,252,152 0
The following example shows the results of loading 1,048,577 rows into a table. The results show that one
COMPRESSED rowgroup in the columnstore (as compressed column segments), and 1 row in the deltastore.
INSERT INTO <columnstore index> SELECT <list of columns> FROM <Staging Table>
This command loads the data into the columnstore index in similar ways to BCP or Bulk Insert but in a single batch.
If the number of rows in the staging table < 102400, the rows are loaded into a delta rowgroup otherwise the
rows are directly loaded into compressed rowgroup. One key limitation was that this INSERT operation was single
threaded. To load data in parallel, you could create multiple staging table or issue INSERT/SELECT with non-
overlapping ranges of rows from the staging table. This limitation goes away with SQL Server 2016. The command
below loads the data from staging table in parallel but you will need to specify TABLOCK
INSERT INTO <columnstore index> WITH (TABLOCK) SELECT <list of columns> FROM <Staging Table>
There are following optimizations available when loading into clustered columnstore index from staging table
Log Optimization: Minimally logged both when the data is loaded into compressed rowgroup. No minimal
logging when data gets loaded into delta rowgroup.
Locking Optimization: When loading into compressed rowgroup, the X lock on rowgroup is acquired.
However, with delta rowgroup, an X lock is acquired at rowgroup but SQL Server still locks the locks
PAGE/EXTENT because X rowgroup lock is not part of locking hierarchy.
If you have or more nonclustered indexes, there is no locking or logging optimization for the index itself but
the optimizations on clustered columnstore index as described above are still there
Note, concurrent threads using INSERT INTO to insert values into a clustered columnstore index can insert rows
into the same deltastore rowgroup.
Once the rowgroup contains 1,048,576 rows, the delta rowgroup us marked closed but it is still available for
queries and update/delete operations but the newly inserted rows go into an existing or newly created deltastore
rowgroup. There is a background thread Tuple Mover (TM) that compresses the closed delta rowgroups
periodically every 5 minutes or so. You can explicitly invoke the following command to compress the closed delta
rowgroup
If you want force a delta rowgroup closed and compressed, you can execute the following command. You may
want run this command if you are done loading the rows and don't expect any new rows. By explicitly closing and
compressing the delta rowgroup, you can save storage further and improve the analytics query performance. A
best practice is to invoke this command if you don't expect new rows to be inserted.
Next steps
For further discussion on loading, see this blog post.
Columnstore indexes - what's new
3/24/2017 • 6 min to read • Edit Online
THIS TOPIC APPLIES TO: SQL Server (starting with 2012) Azure SQL Database Azure SQL Data Warehouse
Parallel Data Warehouse
Summary of columnstore features available for each version of SQL Server, and the latest releases of Azure SQL
Database Premium Edition, Azure SQL Data Warehouse, and Parallel Data Warehouse.
NOTE
For Azure SQL Database, columnstore indexes are only available in Premium Edition.
AlwaysOn yes
readable
secondary
supports
updateable
columnstore
indexes.
*To create a readable nonclustered columnstore index, store the index on a read-only filegroup.
See Also
Columnstore Indexes Guide
Columnstore Indexes Data Loading
Columnstore Indexes Query Performance
Get started with Columnstore for real time operational analytics
Columnstore Indexes for Data Warehousing
Columnstore Indexes Defragmentation
Columnstore indexes - query performance
3/24/2017 • 13 min to read • Edit Online
THIS TOPIC APPLIES TO: SQL Server (starting with 2012) Azure SQL Database Azure SQL Data
Warehouse Parallel Data Warehouse
Recommendations for achieving the very fast query performance that columnstore indexes are designed to
provide.
Columnstore indexes can achieve up to 100x better performance on analytics and data warehousing workloads
and up to 10x better data compression than traditional rowstore indexes. These recommendations will help your
queries achieve the very fast query performance that columnstore indexes are designed to provide. Further
explanations about columnstore performance are at the end.
compute scalar Expression that yes yes yes There are some
evaluates to a restrictions on
scalar value. data type. This is
true for all batch
mode operators.
merge join no no no
nested loops no no no
single-threaded no no yes
queries running
under MAXDOP
1
single-threaded no no yes
queries with a
serial query plan
¹Applies to SQL Server 2016, SQL Database V12 Premium Edition, and SQL Data Warehouse
Aggregate Pushdown
A normal execution path for aggregate computation to fetch the qualifying rows from the SCAN node and
aggregate the values in Batch Mode. While this delivers good performance, but with SQL Server 2016, the
aggregate operation can be pushed to the SCAN node to improve the performance of aggregate computation by
orders of magnitude on top of Batch Mode execution provided the following conditions are met
Supported aggregate operators are MIN, MAX, SUM, COUNT, AVG
Any datatype <= 64 bits is supported. For example, bigint is supported as its size is 8 bytes but decimal
(38,6) is not because its size is 17 bytes. Also, no string types are supported
Aggregate operator must be on top of SCAN node or SCAN node with group by
Aggregate push down is further accelerated by efficient Aggregation on compressed/encoded data in
cache-friendly execution and by leveraging SIMD
SELECT SUM(TotalProductCost)
FROM FactResellerSalesXL_CCI
See Also
Columnstore Indexes Guide
Columnstore Indexes Data Loading
Columnstore Indexes Versioned Feature Summary
Columnstore Indexes Query Performance
Get started with Columnstore for real time operational analytics
Columnstore Indexes for Data Warehousing
Columnstore Indexes Defragmentation
Get started with Columnstore for real time
operational analytics
3/24/2017 • 10 min to read • Edit Online
THIS TOPIC APPLIES TO: SQL Server (starting with 2016) Azure SQL Database Azure SQL Data
Warehouse Parallel Data Warehouse
SQL Server 2016 introduces real-time operational analytics, the ability to run both analytics and OLTP workloads
on the same database tables at the same time. Besides running analytics in real-time, you can also eliminate the
need for ETL and a data warehouse.
Real-time analytics uses an updateable columnstore index on a rowstore table. The columnstore index maintains
a copy of the data, so the OLTP and analytics workloads run against separate copies of the data. This minimizes
the performance impact of both workloads running at the same time. SQL Server automatically maintains index
changes so OLTP changes are always up-to-date for analytics. With this design, it is possible and practical to run
analytics in real-time on up-to-date data. This works for both disk-based and memory-optimized tables.
The columnstore index on an in-memory table allows operational analytics by integrating in-memory
OLTP and in-memory columnstore technologies to deliver high performance for both OLTP and analytics
workloads. The columnstore index on an in-memory table must include all the columns.
Blog Posts
Read Sunil Agarwal's blog posts to learn more about real-time operational analytics. It might be easier to
understand the performance tips sections if you look at the blog posts first.
Business case for real-time operational analytics
Using a nonclustered columnstore index for real-time operational analytics
A simple example using a nonclustered columnstore index
How SQL Server maintains a nonclustered columnstore index on a transactional workload
Minimizing the impact of nonclustered columnstore index maintenance by using a filtered index
Minimizing the impact of nonclustered columnstore index maintenance by using compression delay
Minimizing impact of a nonclustered columnstore index maintenance by using compression delay -
performance numbers
Real time operational analytics with memory-optimized tables
Minimize index fragmentation in a columnstore indes
Columnstore index and the merge policy for rowgroups
NOTE
A filtered nonclustered columnstore index is only supported on disk-based tables. It is not supported on memory-
optimized tables
Example A: Access hot data from btree index, warm data from columnstore index
This example uses a filtered condition (accountkey > 0) to establish which rows will be in the columnstore index.
The goal is to design the filtered condition and subsequent queries to access frequently changing “hot” data
from the btree index, and to access the more stable “warm” data from the columnstore index.
NOTE
The query optimizer will consider, but not always choose, the columnstore index for the query plan. When the query
optimizer chooses the filtered columnstore index, it transparently combines the rows both from columnstore index as well
as the rows that do not meet the filtered condition to allow real-time analytics. This is different from a regular
nonclustered filtered index which can be used only in queries that restrict themselves to the rows present in the index.
-- OrderStatusDesc
-- 0 => 'Order Started'
-- 1 => 'Order Closed'
-- 2 => 'Order Paid'
-- 3 => 'Order Fullfillment Wait'
-- 4 => 'Order Shipped'
-- 5 => 'Order Received'
-- The following query returns the total purchase done by customers for items > $100 .00
-- This query will pick rows both from NCCI and from 'hot' rows that are not part of NCCI
SELECT top 5 customername, sum (PurchasePrice)
FROM orders
WHERE purchaseprice > 100.0
Group By customername
The analytics query will execute with the following query plan. You can see that the rows not meeting the filtered
condition are accessed through clustered btree index.
Please refer to the blog for details on filtered nonclustered columnstore index.
-- Creating nonclustered columnstore index with COMPRESSION_DELAY. The columnstore index will keep the rows
in closed delta rowgroup for 100 minutes
-- after it has been marked closed
CREATE NONCLUSTERED COLUMNSTORE index t_colstor_cci on t_colstor (accountkey, accountdescription,
accounttype)
WITH (DATA_COMPRESSION= COLUMNSTORE, COMPRESSION_DELAY = 100);
If the number of deleted rows in compressed rowgroups > 20%, plateauing in older rowgroups with < 5%
variation (referred to as cold rowgroups) set COMPRESSION_DELAY = (youngest_rowgroup_created_time –
current_time). Note that this approach works best with a stable and relatively homogeneous workload.
See Also
Columnstore Indexes Guide
Columnstore Indexes Data Loading
Columnstore Indexes Query Performance
Columnstore Indexes for Data Warehousing
Columnstore Indexes Defragmentation
Columnstore indexes - data warehouse
3/24/2017 • 4 min to read • Edit Online
THIS TOPIC APPLIES TO: SQL Server (starting with 2016) Azure SQL Database Azure SQL Data
Warehouse Parallel Data Warehouse
Columnstore indexes, in conjunction with partitioning, are essential for building a SQL Server data warehouse.
What’s new
SQL Server 2016 introduces these features for columnstore performance enhancements:
Always On supports querying a columnstore index on a readable secondary replica.
Multiple Active Result Sets (MARS) supports columnstore indexes.
A new dynamic management view sys.dm_db_column_store_row_group_physical_stats (Transact-SQL)
provides performance troubleshooting information at the row group level.
Single-threaded queries on columnstore indexes can run in batch mode. Previously, only multi-threaded
queries could run in batch mode.
The SORT operator runs in batch mode.
Multiple DISTINCT operation runs in batch mode.
Window Aggregates now runs in batch mode for database compatibility level 130
Aggregate Pushdown for efficient processing of aggregates. This is supported on all database
compatibility levels.
String predicate pushdown for efficient processing of string predicates. This is supported on all database
compatibility levels.
Snapshot isolation for database compatibility level 130
Example: Use a nonclustered index to enforce a primary key constraint on a columnstore table
By design, a columnstore table does not allow a primary key constraint. Now you can use a nonclustered index
on a columnstore table to enforce a primary key constraint. A primary key is equivalent to a UNIQUE constraint
on a non-NULL column, and SQL Server implements a UNIQUE constraint as a nonclustered index. Combining
these facts, the following example defines a UNIQUE constraint on the non-NULL column accountkey. The result
is a nonclustered index that enforces a primary key constraint as a UNIQUE constraint on a non-NULL column.
Next, the table is converted to a clustered columnstore index. During the conversion, the nonclustered index
persists. The result is a clustered columnstore index with a nonclustered index that enforces a primary key
constraint. Since any update or insert on the columnstore table will also affect the nonclustered index, all
operations that violate the unique constraint and the non-NULL will cause the entire operation to fail.
The result is a columnstore index with a nonclustered index that enforces a primary key constraint on both
indexes.
--By using the previous two steps, every row in the table meets the UNIQUE constraint
--on a non-NULL column.
--This has the same end-result as having a primary key constraint
--All updates and inserts must meet the unique constraint on the nonclustered index or they will fail.
BEGIN TRAN
-- The query plan chooses a seek operation on the nonclustered index
-- and takes the row lock
SELECT * FROM t_account WHERE AccountKey = 100;
END TRAN
See Also
Columnstore Indexes Guide
Columnstore Indexes Data Loading
Columnstore Indexes Versioned Feature Summary
Columnstore Indexes Query Performance
Get started with Columnstore for real time operational analytics
Columnstore Indexes Defragmentation
Columnstore indexes - defragmentation
3/24/2017 • 5 min to read • Edit Online
THIS TOPIC APPLIES TO: SQL Server (starting with 2012) Azure SQL Database Azure SQL Data
Warehouse Parallel Data Warehouse
Tasks for defragmenting columnstore indexes.
-- Load data
DECLARE @loop int;
DECLARE @AccountDescription varchar(50);
DECLARE @AccountKey int;
DECLARE @AccountType varchar(50);
DECLARE @AccountCode int;
SELECT @loop = 0;
BEGIN TRAN
WHILE (@loop < 300000)
BEGIN
SELECT @AccountKey = CAST (RAND()*10000000 AS int);
SELECT @AccountDescription = 'accountdesc ' + CONVERT(varchar(20), @AccountKey);
SELECT @AccountType = 'AccountType ' + CONVERT(varchar(20), @AccountKey);
SELECT @AccountCode = CAST (RAND()*10000000 AS int);
3. Bulk insert the staging table rows into the columnstore table. INSERT INTO ... SELECT performs a bulk
insert. The TABLOCK runs the insert in parallel.
-- Run this dynamic management view (DMV) to see the OPEN rowgroups.
-- The number of rowgroups depends on the degree of parallelism.
-- You will see multiple OPEN rowgroups depending on the degree of parallelism.
-- This is because insert operation can run in parallel in SQL server 2016.
SELECT *
FROM sys.dm_db_column_store_row_group_physical_stats
WHERE object_id = object_id('cci_target')
ORDER BY row_group_id;
In this example, the results show 8 OPEN rowgroups that each have 37,500 rows. The number of OPEN
rowgroups depends on the max_degree_of_parallelism setting.
5. Use ALTER INDEX REORGANIZE with the COMPRESS_ALL_ROW_GROUPS option to force all rowgroups to
be compressed into the columnstore.
-- This command will force all CLOSED and OPEN rowgroups into the columnstore.
ALTER INDEX idx_cci_target ON cci_target
REORGANIZE WITH (COMPRESS_ALL_ROW_GROUPS = ON);
SELECT *
FROM sys.dm_db_column_store_row_group_physical_stats
WHERE object_id = object_id('cci_target')
ORDER BY row_group_id;
The results show 8 COMPRESSED rowgroups and 8 TOMBSTONE rowgroups. Each rowgroup got
compressed into the columnstore regardless of its size. The TOMBSTONE rowgroups will be removed by
the system.
6. For query performance, its much better to combine small rowgroups together. ALTER INDEX REORGANIZE
will combine COMPRESSED rowgroups together. Now that the delta rowgroups are compressed into the
columnstore, run ALTER INDEX REORGANIZE again to combine the small COMPRESSED rowgroups. This
time you don't need the COMPRESS_ALL_ROW_GROUPS option.
-- Run this again and you will see that smaller rowgroups
-- combined into one compressed rowgroup with 300,000 rows
ALTER INDEX idx_cci_target ON cci_target REORGANIZE;
SELECT *
FROM sys.dm_db_column_store_row_group_physical_stats
WHERE object_id = object_id('cci_target')
ORDER BY row_group_id;
The results show the 8 COMPRESSED rowgroups are now combined into one COMPRESSED rowgroup.
See Also
Columnstore indexes - what's new
Columnstore indexes - query performance
Get started with Columnstore for real time operational analytics
Columnstore indexes - data warehouse