Skip to content

Commit 29ce2ca

Browse files
Merge pull request #34493 from dimitri-furman/dfurman/index-maintenance
Fix example query
2 parents e00a8e7 + 13d60a1 commit 29ce2ca

File tree

1 file changed

+39
-27
lines changed

1 file changed

+39
-27
lines changed

docs/relational-databases/indexes/reorganize-and-rebuild-indexes.md

Lines changed: 39 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
---
2-
title: "Maintaining indexes optimally to improve performance and reduce resource utilization"
2+
title: "Maintaining Indexes Optimally to Improve Performance and Reduce Resource Utilization"
33
description: This article describes index maintenance concepts, and a recommended strategy to maintain indexes.
44
author: dimitri-furman
55
ms.author: dfurman
66
ms.reviewer: mikeray
7-
ms.date: 06/20/2025
7+
ms.date: 06/23/2025
88
ms.service: sql
99
ms.subservice: table-view-index
1010
ms.topic: how-to
@@ -95,7 +95,7 @@ Compressed row group fragmentation in a columnstore index can be computed using
9595
100.0 * (ISNULL(total_stored_deleted_rows, 0)) / NULLIF(total_rows, 0)
9696
```
9797

98-
To determine the total number of physically stored deleted rows for a nonclustered columnstore index, add the value in the `deleted_rows` column in `sys.dm_db_column_store_row_group_physical_stats` to the value in the `rows` column in [sys.internal_partitions](../system-catalog-views/sys-internal-partitions-transact-sql.md) for the internal object type `COLUMN_STORE_DELETE_BUFFER` and the same object, index, and partition. For an example, see [Check the fragmentation of a columnstore index](#check-the-fragmentation-of-a-columnstore-index).
98+
To determine the total number of physically stored deleted rows for a nonclustered columnstore index, add the values in the `deleted_rows` column in `sys.dm_db_column_store_row_group_physical_stats` to the value in the `rows` column in [sys.internal_partitions](../system-catalog-views/sys-internal-partitions-transact-sql.md) for the internal object type `COLUMN_STORE_DELETE_BUFFER` and the same object, index, and partition. For an example, see [Check the fragmentation of a columnstore index](#check-the-fragmentation-of-a-columnstore-index).
9999

100100
> [!TIP]
101101
> For both rowstore and columnstore indexes, review index or heap fragmentation and page density after a large number of rows is deleted or updated. For heaps, if there are frequent updates, review fragmentation periodically to avoid proliferation of forwarding records. For more information about heaps, see [Heaps (Tables without Clustered Indexes)](../../relational-databases/indexes/heaps-tables-without-clustered-indexes.md#heap-structures).
@@ -119,8 +119,9 @@ Reorganizing an index is less resource intensive than rebuilding an index. For t
119119
- For [rowstore indexes](clustered-and-nonclustered-indexes-described.md), the [!INCLUDE [ssDE-md](../../includes/ssde-md.md)] defragments only the leaf level of clustered and nonclustered indexes on tables and views. It physically reorders the leaf-level pages to match the logical order of the leaf nodes, left to right. Reorganizing also compacts index pages to make page density equal to the [fill factor](../../relational-databases/indexes/specify-fill-factor-for-an-index.md) of the index. To view the fill factor setting, use [sys.indexes](../../relational-databases/system-catalog-views/sys-indexes-transact-sql.md). For syntax examples, see [Examples - Rowstore reorganize](../../t-sql/statements/alter-index-transact-sql.md#examples-rowstore-indexes).
120120
- When using [columnstore indexes](columnstore-indexes-overview.md), the delta store can end up with multiple small row groups after inserting, updating, and deleting data over time. Reorganizing a columnstore index forces delta store row groups into compressed row groups in columnstore, and combines smaller compressed row groups into larger row groups. The reorganize operation also physically removes rows that are marked as deleted in the columnstore. Reorganizing a columnstore index can require additional CPU resources to compress data. While the operation is running, performance can slow. However, once data is compressed, query performance improves. For syntax examples, see [Examples - Columnstore reorganize](../../t-sql/statements/alter-index-transact-sql.md#examples-columnstore-indexes).
121121

122-
> [!NOTE]
123-
> <a name="bckmergetsk"></a> Starting with [!INCLUDE [sql-server-2019](../../includes/sssql19-md.md)], [!INCLUDE [ssazure-sqldb](../../includes/ssazure-sqldb.md)], and [!INCLUDE [ssazuremi](../../includes/ssazuremi-md.md)], the tuple-mover is helped by a background merge task that automatically compresses smaller open delta rowgroups that have existed for some time as determined by an internal threshold, or merges compressed rowgroups from where a large number of rows has been deleted. This improves the columnstore index quality over time. For most cases this dismisses the need for issuing `ALTER INDEX ... REORGANIZE` commands.
122+
<a id="bckmergetsk"></a>
123+
124+
Starting with [!INCLUDE [sql-server-2019](../../includes/sssql19-md.md)], [!INCLUDE [ssazure-sqldb](../../includes/ssazure-sqldb.md)], and [!INCLUDE [ssazuremi](../../includes/ssazuremi-md.md)], the tuple-mover is helped by a background merge task that automatically compresses smaller open delta rowgroups that have existed for some time as determined by an internal threshold, or merges compressed rowgroups from where a large number of rows has been deleted. This improves the columnstore index quality over time. For most cases this dismisses the need for issuing `ALTER INDEX ... REORGANIZE` commands.
124125

125126
> [!TIP]
126127
> If you cancel a reorganize operation, or if it is otherwise interrupted, the progress it made to that point is persisted in the database. To reorganize large indexes, the operation can be started and stopped multiple times until it completes.
@@ -338,33 +339,44 @@ For more information, see [sys.dm_db_index_physical_stats](../../relational-data
338339
The following example determines the average fragmentation for all columnstore indexes with compressed row groups in the current database.
339340

340341
```sql
342+
WITH columnstore_row_group_partition
343+
AS (SELECT object_id,
344+
index_id,
345+
partition_number,
346+
SUM(deleted_rows) AS partition_deleted_rows,
347+
SUM(total_rows) AS partition_total_rows
348+
FROM sys.dm_db_column_store_row_group_physical_stats
349+
WHERE state_desc = 'COMPRESSED'
350+
GROUP BY object_id, index_id, partition_number),
351+
/* For nonclustered columnstore, include rows in the delete buffer */
352+
columnstore_internal_partition
353+
AS (SELECT object_id,
354+
index_id,
355+
partition_number,
356+
SUM(rows) AS delete_buffer_rows
357+
FROM sys.internal_partitions
358+
WHERE internal_object_type_desc = 'COLUMN_STORE_DELETE_BUFFER'
359+
GROUP BY object_id, index_id, partition_number)
341360
SELECT OBJECT_SCHEMA_NAME(i.object_id) AS schema_name,
342361
OBJECT_NAME(i.object_id) AS object_name,
343362
i.name AS index_name,
344363
i.type_desc AS index_type,
345-
100.0 * (ISNULL(SUM(rgs.deleted_rows + ISNULL(ip.rows, 0)), 0)) / NULLIF(SUM(rgs.total_rows), 0) AS avg_fragmentation_in_percent
364+
crgp.partition_number,
365+
100.0 * (ISNULL(crgp.partition_deleted_rows + ISNULL(cip.delete_buffer_rows, 0), 0)) / NULLIF (crgp.partition_total_rows, 0) AS avg_fragmentation_in_percent
346366
FROM sys.indexes AS i
347-
INNER JOIN sys.dm_db_column_store_row_group_physical_stats AS rgs
348-
ON i.object_id = rgs.object_id
349-
AND
350-
i.index_id = rgs.index_id
351-
/* For nonclustered columnstore, include rows in the delete buffer */
352-
LEFT JOIN sys.internal_partitions AS ip
353-
ON i.object_id = ip.object_id
354-
AND
355-
i.index_id = ip.index_id
356-
AND
357-
rgs.partition_number = ip.partition_number
358-
AND
359-
ip.internal_object_type_desc = 'COLUMN_STORE_DELETE_BUFFER'
360-
WHERE rgs.state_desc = 'COMPRESSED'
361-
GROUP BY i.object_id, i.index_id, i.name, i.type_desc
362-
ORDER BY schema_name, object_name, index_name, index_type;
367+
INNER JOIN columnstore_row_group_partition AS crgp
368+
ON i.object_id = crgp.object_id
369+
AND i.index_id = crgp.index_id
370+
LEFT OUTER JOIN columnstore_internal_partition AS cip
371+
ON i.object_id = cip.object_id
372+
AND i.index_id = cip.index_id
373+
AND crgp.partition_number = cip.partition_number
374+
ORDER BY schema_name, object_name, index_name, partition_number, index_type;
363375
```
364376

365-
The previous statement returns a result set similar to the following:
377+
The previous statement returns a result set similar to the following output:
366378

367-
```
379+
```output
368380
schema_name object_name index_name index_type avg_fragmentation_in_percent
369381
------------ ---------------------- ------------------------------------ ------------------------- ----------------------------
370382
Sales InvoiceLines NCCX_Sales_InvoiceLines NONCLUSTERED COLUMNSTORE 0.000000000000000
@@ -450,7 +462,7 @@ For more information, see [ALTER INDEX](../../t-sql/statements/alter-index-trans
450462
- [Adaptive Index Defrag](https://github.com/Microsoft/tigertoolbox/tree/master/AdaptiveIndexDefrag)
451463
- [CREATE STATISTICS (Transact-SQL)](../../t-sql/statements/create-statistics-transact-sql.md)
452464
- [UPDATE STATISTICS (Transact-SQL)](../../t-sql/statements/update-statistics-transact-sql.md)
453-
- [Columnstore indexes - Query performance](columnstore-indexes-query-performance.md)
454-
- [Get started with Columnstore for real-time operational analytics](get-started-with-columnstore-for-real-time-operational-analytics.md)
455-
- [Columnstore indexes - Data Warehouse](columnstore-indexes-data-warehouse.md)
465+
- [Columnstore indexes - query performance](columnstore-indexes-query-performance.md)
466+
- [Get started with columnstore indexes for real-time operational analytics](get-started-with-columnstore-for-real-time-operational-analytics.md)
467+
- [Columnstore indexes in data warehousing](columnstore-indexes-data-warehouse.md)
456468
- [Columnstore indexes and the merge policy for row groups](/archive/blogs/sqlserverstorageengine/columnstore-index-merge-policy-for-reorganize)

0 commit comments

Comments
 (0)