SQL Server: Vistas indizadas y el porqué de usarlas para cargas de dwh

Creación de nueva vista desde la vista DiesñoLas vistas pueden ser una herramienta perfecta para simplificar consultas que unen distintas tablas, permiten abstenerse de la estructura origen y simplificar si hace falta la estructura saliente. También nos pueden servir como un mecanismo de seguridad que limitará el acceso de los usuarios a datos no deseados de las tablas base. Con MS SQL Server, Microsoft ha ido un paso más allá y permite crear un nuevo tipo de vistas llamado vista indizada.

Que son las vistas indizadas?

Las vistas de toda la vida, las podemos ver como si fueran una tabla pero realmente no existe. Una vista estandar no está almacenada en la base de datos, en lugar de eso los datos que se devuelven se recuperan de forma dinámica en el momento de la petición (la consulta SQL sobre la vista). Una vista indizada se diferencia de una normal basicamente en que sobre la primera creamos un índice (el primero clusterizado). En el momento en que lo hacemos estamos persistiendo en la base de datos una referencia que abre la veda a posibles optimizaciones que no se pueden hacer con una vista simple. También es cierto que si nos hace falta y realmente nos aporta algo también es posible es crear más de un indice ( a partir del segundo ya no pueden ser clústerizados, crear uno clústerizado es obligado).

Una de los beneficios que también da la vista indizada es que aporta una nueva perspectiva en el momento del cálculo para el plan de ejecución de una SELECT sobre las tablas que implica. El optimizador de consultas puede seleccionar la vista si determina que ésta puede sustituirse por parte o por toda la consulta del plan de consultas si es de un coste menor. En el segundo caso, la vista indizada se utiliza en lugar de las tablas subyacentes y sus índices. No es necesario hacer referencia a la vista en la consulta para que el optimizador de consultas la utilice durante la ejecución. Esto incluso permite que las aplicaciones existentes se beneficien de las vistas indizadas recién creadas sin cambiar directamente código en dichas aplicaciones.

Donde usarlas y como crearlas

Las vistas indizadas aportan un beneficio aunque también un coste. Puede tener el mismo “defecto” que puede producir la indexación masiva de una tabla transaccional con inserciones/modificaciones masivas. Debemos evaluar siempre el beneficio en base al coste que suponga. Realmente con las vistas indizadas (clusterizada o no) seguro que notaremos una mejora si las usamos en datawarehouses, data marts, bases de datos OLAP, en procesos de minería de datos y similares. En estos escenarios son candidatas las consultas gigantes sobre diferentes tablas, con particiones verticales u horizontales, agregaciones y sin pensarlo demasiado, las particiones de tablas de hechos. Un ejemplo que se me ocurre es el del diagrama en estrella del datamarts de ventas donde podemos tener cabeceras de venta en una tabla y el detalle en otra. Seria normal en casos como estos haber creado particiones en el grupo de medidas y hacer la separación por las fechas de venta que estan en la cabecera.

 

Una de los requisitos para poder indizar una vista es que tenemos que crear la vista con la opción WITH SCHEMABINDING. Esta opción tiene su lado positivo, si el propietario de cualquiera de las tablas incluidas en la select intenta hacer un cambio en la estructura no podrá. Esto es fantástico porque nos protege de cualquier cambio a traición de las tablas.

Pongo aquí un ejemplo básico. Creamos primero la tabla y la vista con la selección de los campos que nos interesan.

CREATE TABLE Articulos (
IdArticulo INT PRIMARY KEY,
Descripcion VARCHAR(20),
Stock INT)
GO

 

CREATE VIEW ArticulosView WITH SCHEMABINDING AS
SELECT IdArticulo, Stock
FROM dbo.Articulos
WHERE Stock > 0
GO

CREATE UNIQUE CLUSTERED INDEX idx_ArticulosView ON ArticulosView(Stock)

Una vez hemos creado el índice, los datos de la vista están almacenados en la base de datos como cualquier otro índice clusterizado sobre una tabla. Cualquier consulta sobre la vista ya puede usar el índice para el cálculo del plan de ejecución. Consultas que tengan un predicado similar ya podran beneficiarse del indice por rango. Por ejemplo:

SELECT IdArticulo, Stock
FROM Articulos
WHERE Stock > 0

Incluso en este caso y aunque no hagamos la consulta sobre la vista, el optimizador de consultas puede tener en cuenta el indice clusterizado de la vista para obtener los datos con un mejor rendimiento que si lo hiciera con los indices de la tabla base. 

Otro ejemplo con más sentido donde se usa el indice clusterizado de la vista en lugar del de la tabla. Nos imaginamos que queremos hacer una partición vertical de una tabla con mucho campos (wide_tbl) que podría ser la tabla de cabeceras o lineas de venta de nuestro erp desde el que sacamos directamente la información que nos interesa de la parte de ventas:

– Creación de la tabla base
CREATE TABLE wide_tbl(
a int PRIMARY KEY, b int, c int, d int, e int, f int, g int, h int, i int,
j int, k int, l int, m int, n int, o int, p int, q int, r int,s int,t int, u int, v int, w int, z int)
GO

– Vista sobre la tabla base donde hacemos partición vertical (nos quedamos solo con lo que nos interesa)
CREATE VIEW v_abc WITH SCHEMABINDING AS
SELECT a, b, c
FROM dbo.wide_tbl
WHERE a BETWEEN 0 AND 1000
GO
CREATE UNIQUE CLUSTERED INDEX i_abc ON v_abc(a)
GO

La select es la siguiente. Si miramos el plan de ejecución estimado ya vemos que está usando efectivamente el índice de la vista aunque seleccionemos datos de la tabla.

SELECT b, count_big(*), SUM(c)
FROM wide_tbl
WHERE a BETWEEN 0 AND 1000
GROUP BY b

Plan de ejecución usando el índice de la vista indizada en lugar del propio de la tabla base.

A tener en cuenta cuando creamos indices sobre vistas

Ademas de incluirse la clausula WITH SCHEMABINDING que nos prohibe la modificación de la estructura o eliminación de las tablas bases incluidas en la vista, debemos pensar o tener en cuenta que:

  • La vista debe ser determinista. Es decir, siempre debe devolver el mismo resultado para el mismo input. No se pueden incluir por ejemplo funciones como GETDATE que devuelven valores distintos para diferentes llamadas con el mismo argumento. 
  • WITH SCHEMABINDING, con sus cosas buenas y sus cosas malas.
  • En la definición de la vista se debe hacer referencia a nombre completo la tabla (schema.tabla). Lo mismo con las funciones de usuario.
  • Espacio en disco. Debemos recordar que los indices que creemos ocuparan el mismo espacio en disco que cualquier otro sobre una tabla.
  • A diferencia de Oracle, donde un equivalente podría ser una vista materializada, no se necesita ningún permiso concreto para este tipo de vista. Basta con tener permisos CREATE VIEW y ALTER en el schema donde se crea la vista.

En Conclusión...

... las vistas indizadas pueden ofrecer grandes mejoras de rendimiento pero siempre en los entornos adecuados. Debemos evitarlas en entornos transaccionales con mucha carga transaccional y dejarlas más para otros entornos más de consulta/actualización como puede ser las tablas sobres las que procesamos nuestros cubos del datawarehouse. También es cierto que una alternativa funcional a una vista indizada es un COVER INDEX que incluya las columnas que nos interesen.