You are on page 1of 11

Capítulo 4

Gestión de los índices – nivel
avanzado

Objetivo

Analizar algunas técnicas avanzadas de indexación.
Identificar los índices especiales disponibles en SQL Server.

Temas

1.
2.

Técnicas avanzadas de indexación
Tipos de índices especiales.

Cibertec Perú S.A.C - Transact-SQL - SQL Server 2014

Gestión de los índices – nivel avanzado

1.

34

Técnicas avanzadas de indexación

 Covering Indexes
Será revisado mediante un ejemplo.
1. En la tabla Person.Address, ejecutar lo siguiente:
sp_helpindex 'Person.Address'

2. Registrar la información obtenida. Ejecutar la sentencia.
set statistics io on
go
Select
a.PostalCode
from
Person.Address a
where a.StateProvinceID = 42
go
set statistics io off;

3. Revisar la información estadística y los Planes de Ejecución. Luego, registrar dicha
información.
Como se observa en el resultado final, sí se utiliza el índice; sin embargo, si query
solo retornace la columna StateProvinceID, no se tendría mayor problema. Se podría
realizar el cambio en el query anterior para satisfacer este requerimiento,
lastimosamente el query requiere otro campo que forma parte del índice, una posible
solución sería la de incluir el campo PostalCode dentro del índice de la columna
StateProvinceID, sin embargo, si más adelante se requiere otro campo, sería
necesario agregarlo y así sucesivamente, lo que ocasionaría un índice con

Cibertec Perú S.A.C - Transact-SQL - SQL Server 2014

Gestión de los índices – nivel avanzado

35

demasiados campos y que no se podría reusar para otros queries solo para éste, por
la cantidad de columnas que forman parte del índice.
Para este caso se presenta un key lookup, para poder traer el valor de PostalCode,
que es necesario para satisfacer el query,
4. Modificar el índice existente para poder cubrir la consulta solicitada, ejecutar el
siguiente script:
create nonclustered index IX_Address_StateProvinceID
on
person.Address (StateProvinceID asc)
Include (PostalCode)
with (DROP_EXISTING = ON)

5. Revisar la información estadística del query, en el Plan de Ejecución.
set statistics io on
go
Select
a.PostalCode
from
Person.Address a
where a.StateProvinceID = 42
go
set statistics io off;

Se puede observar una gran disminución de los reads y el uso del índice satisface el query. Al
volver a revisar los índices de la tabla, se verá que:
sp_helpindex 'Person.Address'
Se adicionan columnas al índice usando INCLUDE, esto sin tener que alterar el número de
columnas del índice, porque se almacenan solo a nivel de la última hoja del árbol del índice.
La opción INCLUDE puede ser utilizada:


Cuando no se quiere incrementar el tamaño de las keys del índice, pero se quiere tener un
covering index.
Cuando se está indexando un tipo de dato que no puede ser indexado (excepto ntext, text,
image)
Cuando ya se ha excedido, el máximo número de key columns para un índice.

Antes de construir una gran cantidad de covering indexes, considerar cómo SQL Server puede
crear covering indexes “on the fly” usando index intersection.

1.2

Index intersections
Usar múltipĺes índices nonclustered para satisfacer todos los requerimientos de
columnas de una tabla para un query.
Si una tabla tiene múltiples índices, entonces SQL Server tratará de usarlos para ejecutar
un query.
Se analizará el siguiente query, para ello, se debe obtener un Plan de Ejecución
Select
*
from
Sales.SalesOrderHeader soh
where soh.SalesPersonID = 276
and soh.OrderDate between '4/1/2007' and '7/1/2007'

Cibertec Perú S.A.C - Transact-SQL - SQL Server 2014

Gestión de los índices – nivel avanzado

36

Se puede apreciar que para la cláusula Where, la tabla debe de tener un índice sobre
ambos casos para que pueda ser utilizada; sin embargo, si se analizan los índices
existentes. Para dicha tabla se puede observar lo siguiente:
sp_helpindex 'Sales.SalesOrderHeader'
La tabla tiene un índice nonclustered sobre la columna SalesPersonID, pero no tiene un
índice sobre la columna OrderDate.
Revisando el Plan de Ejecución previamente obtenido, se observa que el índice que
existe sobre la columna SalesPersonID no está siendo usado, puesto que se necesita
también el valor de la columna OrderDate, por lo cual el Query Optimizer de SQL Server
decide utilizar al índice clustered para retornar todos los valores y realizar el filtro
respecitvo del Where.
Lo ideal sería agregar la columna OrderDate al índice, pero se puede afectar por la
modificación a otros queries, pero, por qué mejor no crear un índice nonclustered sobre
la columna que falta, para ello, ejecutar el siguiente script:
Create
nonclustered
index
Sales.SalesOrderHeader(OrderDate)SalesPersonID,OrderDate

IDX

on

Volver a revisar el Plan de Ejecución del siguiente query:
Select
*
fromSalesPersonID,OrderDate
Sales.SalesOrderHeader soh
where soh.SalesPersonID = 276
and soh.OrderDate between '4/1/2007' and '7/1/2007'

Ahora, se puede apreciar que SQL Server aprovecha ambos índices para obtener la
información con este tipo de estrategia de indexación, podemos aprovechar cada índice
por separado en otras consultas o ambas como en este caso.
Eliminar el índice creado.
drop index IDX on Sales.SalesOrderHeader

1.3

Index joins
Es una variación de la técnica anterior; para poder apreciar mejor esta técnica usaremos
el siguiente query.
Select
soh.SalesPersonID, soh.OrderDate
from
Sales.SalesOrderHeader soh
where soh.SalesPersonID = 276
and soh.OrderDate between '4/1/2005' and '7/1/2005'

Se obtiene el Plan de Ejecución y se observa que, al igual que el caso anterio,r el Query
Optimizer no utiiliza el índice existente sobre SalesPersonID (para revisar los índices
utilizar el procedimiento almacenado de sistem sp_HelpIndex), por lo cual se debe
decidir, utilizar el índice clustered creado.

Cibertec Perú S.A.C - Transact-SQL - SQL Server 2014

Gestión de los índices – nivel avanzado

37

Para poder analizar mejor esta estrategia es necesario obtener la información estadística
de lecturas, para lo cual se ejecuta el query anterior dentro de:
set statistics io on y set statistics io off
(*) Registrar el valor obtenido en el tab messages.
Y ahora, se creará el índice sobre la columna OrderDate para poder tener, como en el
caso anterior, intersección de índices.
create nonclustered index IDX on Sales.SalesOrderHeader(OrderDate)

Revisar nuevamente, el plan de ejecución del query y la información estadística de
lecturas, para lo cual se utiliza el siguiente query:
set statistics io on
go
Select
soh.SalesPersonID, soh.OrderDate
from
Sales.SalesOrderHeader soh
where soh.SalesPersonID = 276
and soh.OrderDate between '4/1/2005' and '7/1/2005'
go
y set statistics io off

Se observa gran disminución de lecturas lógicas, debido a que los dos índices actúan
como un covering index, reduciendo las lecturas lógicas a 2 index seeks en lugar de un
scan al indice clustered.
Ahora a eliminar el índice creado.
drop index IDX on Sales.SalesOrderHeader

1.4

Filtered Indexes
Es un índice nonclustered que usa un filtro, básicamente una sentencia WHERE, por
ejemplo una columna con muchos valores NULL, se puede tener un filtered index sobre
la columna para tener un índice solo sobre la data que no es NULL.
Por ejemplo, la tabla Sales.
Sales.SalesOrderHeader, tiene más de 30 000 filas, de las cuales más del 25% de filas
tienen valores NULL en las columnas PurchaseOrderNumber y SalesPersonId, si se
desea obtener la siguiente información de ventas con el query que se muestra a
continuación, revisar su plan de ejecución y la información estadística:
set statistics io on
go
Select
soh.PurchaseOrderNumber,
soh.OrderDate,
soh.ShipDate,
soh.SalesPersonID
from Sales.SalesOrderHeader soh
where soh.PurchaseOrderNumber like 'PO5%'
and soh.SalesPersonID is not null
go
set statistics io off;

Cibertec Perú S.A.C - Transact-SQL - SQL Server 2014

Gestión de los índices – nivel avanzado

38

Revisando la información estadística y el Plan de Ejecución es recomendable crear un
índice nonclustered, incluyendo las otras columnas para cubrir el query. Para esto, se
creará el siguiente índice:
create nonclustered index IDX on Sales.SalesOrderHeader(purchaseOrderNumber, SalesPersonId)
INCLUDE (OrderDate,ShipDate);

Al volver a revisar el Plan de Ejecución y las estadísticas, se puede observar que
mejoraron las lecturas y que ahora se está utilizando el índice creado. Para mejorar el
query, el dato inicial de tener más del 75% de valores nulls para algunos campos es de
vital importancia, ya que con dicha información se puede ajustar al índice para que filtre
los valores null, reduciendo el número de páginas a ser utilizadas por el índice, para ello,
modificar el índice con lo siguiente:
Create nonclustered index IDX on Sales.SalesOrderHeader(purchaseOrderNumber, SalesPersonId)
Include (OrderDate, ShipDate)
Where purchaseOrderNumber IS NOT NULL
And SalesPersonId IS NOT NULL WITH (DROP_EXISTING = ON)

Volver a revisar el query original con el plan de ejecución y la información estadística:
set statistics io on
go
Select
soh.PurchaseOrderNumber,
soh.OrderDate,
soh.ShipDate,
soh.SalesPersonID
from Sales.SalesOrderHeader soh
where soh.PurchaseOrderNumber like 'PO5%'
and soh.SalesPedrop index Sales.SalesOrderHeader.IDXrsonID is not null
go
set statistics io off;

Si bien el plan de ejecución no ha tenido mayores cambios, sí se redujo de 5 a 4 las
lecturas, este impacto no es tan grande para este ejemplo, es del 20%, pero al hablar de
un query que demora minutos un 20%, es considerable.
Ahora, se debe eliminar el índice.
drop index IDX on Sales.SalesOrderHeader

1.5

Index Compression
La compresión de índices y de data fue introducida en la versión 2008 (Edición
Enterprise y Develoer).
Tener una compresión de índices es, básicamente, tener más información en una sola
página de datos, esto significa tener que utilizar menos páginas; sin embargo, es un
arma de doble filo pues se puede tener menos páginas para almacenar un índice, pero
impacta en CPU y Memoria, que son los recursos más utilizados al momento de la
compresión y descompresión.

Cibertec Perú S.A.C - Transact-SQL - SQL Server 2014

Gestión de los índices – nivel avanzado

39

Crear los siguientes índices:
CREATE NONCLUSTERED INDEX IDX1
ON Person.Address(City ASC, PostalCode ASC) ;
CREATE NONCLUSTERED INDEX IDX2
ON Person.Address (City,PostalCode)
WITH (DATA_COMPRESSION = ROW ) ;
CREATE NONCLUSTERED INDEX IDX3
ON Person.Address (City,PostalCode)
WITH (DATA_COMPRESSION = PAGE) ;

Para observar cómo se encuentra el almacenamiento de los índices, a nivel de página de
datos, ejecutar el siguiente query.
SELECT i.Name,
i.type_desc,
s.page_count,
s.record_count,
s.index_level,
compressed_page_count
FROM sys.indexes i
JOIN sys.dm_db_index_physical_stats(DB_ID(N'AdventureWorks2008R2'),
OBJECT_ID(N'Person.Address'), NULL,
NULL, 'DETAILED') AS s
ON i.index_id = s.index_id
WHERE i.OBJECT_ID = OBJECT_ID(N'Person.Address') ;

Para poder revisar el número de lecturas por cada índice, se debe forzar al query original
para que utilice un determinado índice y ejecutar cada una de las siguientes sentencias,
registrando los valores estadísticos de las lecturas para armar un cuadro comparativo al
final:
SELECT a.City,
a.PostalCode
FROM Person.Address AS a WITH (INDEX = IDX1)
WHERE a.City = 'Newton'
AND a.PostalCode = 'V2M1N7' ;
SELECT a.City,
a.PostalCode
FROM Person.Address AS a WITH (INDEX = IDX2)
WHERE a.City = 'Newton'
AND a.PostalCode = 'V2M1N7' ;
SELECT a.City,
a.PostalCode
FROM Person.Address AS a WITH (INDEX = IDX3)
WHERE a.City = 'Newton'
AND a.PostalCode = 'V2M1N7' ;

Cibertec Perú S.A.C - Transact-SQL - SQL Server 2014

Gestión de los índices – nivel avanzado

40

Cuadro comparativo:
Escenario

Información estadística

IDX1

Scan count ___, logical reads ___, physical reads __, read-ahead
reads __, lob logical reads __, lob physical reads __, lob read-ahead
reads __.

IDX2

Scan count ___, logical reads ___, physical reads __, read-ahead
reads __, lob logical reads __, lob physical reads __, lob read-ahead
reads __.

IDX3

Scan count ___, logical reads ___, physical reads __, read-ahead
reads __, lob logical reads __, lob physical reads __, lob read-ahead
reads __.

Ahora, eliminar los índices creados anteriormente; para ello, ejecutar el siguiente query:
DROP INDEX Person.Address.IDX1;
DROP INDEX Person.Address.IDX2;
DROP INDEX Person.Address.IDX3;

1.6

ColumnStore Index
Esta nueva estrategia de indexación es propia de la versión SQL Server 2012. Este tipo
de índice, realiza la indexación de la data por columnas, en lugar de hacerlo por filas,
esta nueva característica es muy útil y agrega mejor rendimiento a los entornos de
datawarehouse, donde se tiene gran cantidad de data en las tablas.
La información almacenada dentro de un índice de tipo ColumnStore, es agrupada en
cada columna y estos agrupamientos son almacenados individualmente.
Algunas limitaciones cuando se utilizan los índices ColumnStore son:



Las columnas que tienen este tipo de índices no pueden ser actualizados (lo que es
un comportamiento normal de las bases de datos datawarehouse), para poder
realizar una actualizacion, primero se debe eliminar el índice ColumnStore.
No se puede utilizar ciertos tipos de datos en estos índices como: binary, text,
varchar(max), uniqueidentifier, xml, tipos de datos clr o decimales con una precisión
mayor a 18.
No se puede crear un índice ColumnStore sobre una columna Sparse.
No se puede crear este tipo de índices sobre vistas.

En la base de datos AdventureWorks2008R2, una tabla que sirve para almacenar
información histórica, no sufre actualizaciones constantes, pero sí cargas y lecturas
constantes, con lo cual es un buen candidato para tener un índice ColumnStore, este es
el query que se consulta habitualmente:
Set statistics io on
go
SELECT tha.ProductID,
COUNT(tha.ProductID) AS CountProductID,
SUM(tha.Quantity) AS SumQuantity,
AVG(tha.ActualCost) AS AvgActualCost
FROM Production.TransactionHistoryArchive AS tha
GROUP BY tha.ProductID ;
go
Set statistics io on

Cibertec Perú S.A.C - Transact-SQL - SQL Server 2014

Gestión de los índices – nivel avanzado

41

Registrar la información estadística de lecturas y analizar el plan de ejecución. Crear el
índice columnstore, para lo cual se ejecuta el siguiente script:
CREATE NONCLUSTERED COLUMNSTORE INDEX ix_csTest
ON Production.TransactionHistoryArchive
(ProductID,
Quantity,
ActualCost) ;

Con el índice columnStore creado, el query optimizer ahora tiene la opción de usar ese
índice para satisfacer el query previo. Para obtener el beneficio de usar este nuevo tipo
de índice, se debe de ejecutar el query:
Set statistics io on
go
SELECT tha.ProductID,
COUNT(tha.ProductID) AS CountProductID,
SUM(tha.Quantity) AS SumQuantity,
AVG(tha.ActualCost) AS AvgActualCost
FROM Production.TransactionHistoryArchive AS tha
GROUP BY tha.ProductID ;
go
Set statistics io on

La reducción radical de lecturas solicitadas para obtener el resultado del query, es la que
permitirá obtener, de manera más rápida, la información usando un índice columnStore,
ahora esta tabla está indexada por columnas en lugar de filas.

Cibertec Perú S.A.C - Transact-SQL - SQL Server 2014

Gestión de los índices – nivel avanzado

2.

42

Tipos de índices especiales

Full-Text
Para poder almacenar gran cantidad de texto en SQL Server se utiliza el tipo de dato
Varchar, Nvarchar, Char y Nchar con el valor MAX, un índice clustered o nonclustered
no puede ser creado sobre ese tipo de columnas. Para poder indexar este tipo de
columnas y obtener búsquedas sobre éstas, de manera más rápida se tiene que
implementar la búsqueda por Full-Text, utilizando el full text engine se puede crear
índices de tipo full-text, también se pueden crear índices de este tipo sobre tipos de
datos Varbinary o Bigint.
Con este tipo de índice las consultas se pueden realizar de manera similar a la del
buscador google, es decir, al colocar una palabra, la búsqueda ubicará las palabras que
se asemejen, esto dependiendo del idioma con el cual se realizó la configuración del
FullText Srarch.

Spatial
Presentado con SQL Server 2008, ahora es posible almacenar datos espaciales
(spatial data), este tipo de datos puede ser de tipo geométrico o de tipo geográfico
(literalmente especificando un punto en la tierra); realizar una indexación sobre una
columna con este tipo dato es complicado, por lo cual, se debe de crear un índice
especifico.
Un índice espacial solo se puede crear sobre columnas de este tipo; la tabla base no
debe tener vistas indexadas asociadas, solo debe de tener un primary key, además, se
pueden crear hasta 249 índices espaciales sobre alguna columna de una tabla.

Cibertec Perú S.A.C - Transact-SQL - SQL Server 2014

Gestión de los índices – nivel avanzado

43

XML
Presentado como tipo de dato en SQL Server 2005.
XML es almacenado como data de texto well-formed dentro de SQL Server, la data de
estas columnas puede ser consultada usando xQuery; respecto a la indexación existen
algunos que han sido definidos y creados para este tipo de dato, Una columna con un
tipo de dato XML puede tener un principal y varios secundarios índices, el XML primario
parte las propiedades, atributos y elementos de la data del XML y lo almacena como
una tabla interna. Debe existir un primary key en la tabla y debe de ser clustered para
poder crear un índice XML, después que el índice XML es creado, los índices
secundarios pueden ser creados, éstos tienen información como los Paths, Values,
Properties del XML.

Cibertec Perú S.A.C - Transact-SQL - SQL Server 2014