Blog Maxi Accotto

Blog , cursos, coaching y Consultoria en SQL Server

 

 

 

Clustered Index y performance

El Clustered Index es uno de los índices mas importantes que tenemos, y una de sus razones es que solo podemos disponer de uno por tabla y todo tipo de consultas que utilicen rango de valores serán muy beneficiadas por este tipo de índices.

Ahora bien, por defecto cuando creamos una Primary Key se genera de forma automática por esos campos el Clustered Index de nuestra tabla.

Muchos desarrolladores que hacen diseños de tablas prestan poca atención a este detalle. En este Post vamos a analizar los impactos de performance que podemos tener con solo el hecho de no definir de forma adecuada nuestro CLustered Index.

Si creo una tabla (o la modifico) desde el modo de diseño, cuando agregue una primary key veremos una ventana como la siguiente

image

Aquí podemos observar que la opción de “Create as Clustered” esta en Yes para nuestra Primary Key.

También sucede lo mismo si lo hacemos vía código como muestra el siguiente ejemplo

use tempdb 
go

drop table movimientos
drop table articulos
drop table numeros
drop table fechas

create table movimientos (id bigint identity PRIMARY KEY,
                          articulo varchar(50),
                          fecha datetime,
                          cantidad int
                         )
go 

sp_help movimientos

image

Bien lo que haremos ahora es crear una tabla similar a la anterior pero que la primary key no sea el Clustered, porque? porque en mi tabla de movimientos las consultas de rango son de fecha a fecha y no de ID, con lo cual si pongo un indice CLustered para este tipo de consultas voy a mejorar su tiempo de respuesta.

Para ello primero creamos una copia de movimientos con SELECT INTO para no copiar índices

select * 
into movimientos2 from movimientos 
 

Ahora crearemos el indice Clustered por la columna Fecha y un indice Non Clustered por ID

create clustered index ix3 on movimientos2(fecha)
create index ix4 on movimientos2(id)

También le vamos a crear a la tabla movimientos un indice por fecha ya que no lo tenia pero en este caso será en Non Clustered

create index ix2 on movimientos(fecha)

Con el siguiente código veremos como han quedado cada una de estas tablas con relación a los índices

exec sp_helpindex movimientos
exec sp_helpindex movimientos2

image

Aquí podemos observar bien que ambas tablas tienen índices por los mismos campos solamente con la diferencia del Clustered que en la segunda lo hemos puesto por fecha ya que es una tabla la cual sus querys son muy usadas por ese campo y en rangos (una clásica tabla de movimientos)

Bien, ahora lo que haremos es ejecutar una query en ambas tablas (la misma Query) y ver los resultados del Query Plan

 

select articulo 
from movimientos 
where fecha >= '20091101'
and fecha < '20100101'

select articulo 
from movimientos2
where fecha >= '20091101'
and fecha < '20100101'

image

Como podemos observar el primer Query representa un 94% del tiempo total de ambas ejecuciones y el segundo un 6%, siendo este ultimo mucho mas performante.

Aquí podemos  ver lo que ha sucedido con solo el hecho de aplicar el clustered a la columna adecuada, y como mensaje: es muy pero muy importante definir bien el Clustered , este post esta destinado a mostrar que dejarlo por Defecto no es una buena idea y que cada tabla debería tener un estudio de que columnas deberían ser el Clustered Index

Aquí también les dejo el Script completo para que puedan jugar con el mismo y ver los resultados.

Currently rated 5.0 by 2 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Categories: Tunning
Posted by maccotto on Friday, November 13, 2009 10:03 AM
Permalink | Comments (0) | Post RSSRSS comment feed

Order By y efectos en la performance

Todos conocemos la sentencia Order By de TSQL (Transact SQL), de hecho si necesitamos garantizar un orden el uso del Order By es la única forma de hacerlo.

Ahora bien, en este post nos dedicaremos a mostrar los impactos en la performance si es que no se tienen las cosas de forma adecuada , como así también haremos un best practica del Order By.

El primer paso será mostrar como funciona y sus efectos, para ello crearemos una tabla temporal la cual llenaremos con datos de la tabla “Sales.SalesOrderHeader” de nuestra base de ejemplos Adventureworks

use AdventureWorks2008 
go

create table #t1 (id int)

insert into #t1 values 
(0),(1),(2),(3),(4),
(5),(6),(7),(8),(9)

select * 
into #tablaorder
from 
Sales.SalesOrderHeader 
cross join
#t1

Aquí tenemos una nueva tabla (sin ningún tipo de índices) la cual tiene los registros de Sales.SalesOrderHeader por 10 (para ello hicimos un Cross Join contra una tabla auxiliar)

Bien, ahora lo que haremos es crearle un índice Clúster

create clustered index cl1 on #tablaorder(Salesorderid)
go

Ahora ya tenemos nuestra tabla con un solo índice por numero de orden y que a su vez es Clúster.

Que haremos ahora, pues algo simple, empezaremos a ejecutar consultas contra nuestra tabla, la primera que haremos será sin Ordenamiento y la segunda será con Ordenamiento sobre la columna del índice Clúster, en ambas Querys mostrare el plan de ejecución

select t1.salesorderid from #tablaorder t1
where t1.salesorderid > = 60000

image

select t1.salesorderid from #tablaorder t1
where t1.salesorderid > = 60000
order by t1.salesorderid 

image

Como podemos observar en ambos casos el plan de ejecución es el mismo, o sea, aplicar un Order By sobre una columna que tiene un índice no genero mas trabajo.

Ahora lo que haremos es justamente ejecutar una query con un Order By sobre una columna sin índice (por ejemplo OrderDate)

select t1.salesorderid,t1.orderdate
from #tablaorder t1
where t1.salesorderid > = 60000
order by t1.orderdate 

 

image

Ya aquí podemos observar que se agrego una nueva operación (Sort) la cual representa el 60% de todo el Query Plan, si hacemos la comparativa entre los dos Query Plan (sin Order By y con Order By sobre OrderDate) vamos a encontrarnos con lo siguiente

image

Aquí podemos observar que del total del tiempo (ambas querys) la segunda y la que contiene el Order By se lleva el 76% lo cual indica que es mucho mas lenta que la primera que no tiene un Order By.

Esto porque se da? pues como vimos estamos ordenando por una columna sin índices, ahora lo que haremos es crearle un índice por OrderDate a la tabla y volver a correr las Querys

create index cl2 on #tablaorder(orderdate)
 
select t1.salesorderid,t1.orderdate
from #tablaorder t1
where t1.salesorderid > = 60000

select t1.salesorderid,t1.orderdate
from #tablaorder t1
where t1.salesorderid > = 60000
order by t1.orderdate 
 
image 
 
Ahora podemos ver que ambas querys tardan lo mismo y que la operación de Sort ha desaparecido gracias al índice que 
hemos creado.
 
Conclusiones:
 
El Order By es una sentencia muy útil pero tenemos que tener nuestros cuidados, y aquí viene la parte para reflexionar:
Cuando usamos Order By existe la posibilidad que el usuario cambie el Orden luego? o es importante el Orden en lo que
estamos retornando? pensemos que cada vez que ponemos un Order By podemos estar penalizando el rendimiento si no 
tenemos los índices adecuados y muchas veces los Order By que usamos no tienen sentido ya que se pueden hacer
desde el reporte o la aplicación, entonces no le demos una sobrecarga el motor (vimos que hasta un 60% represento)
si es que no tiene sentido, es mas les cuento que en mi experiencia he resuelto algunos temas de performance en querys
simplemente por eliminar un Order By innecesario, recordemos que los Order By los podemos hacer muchas veces en
la grilla de nuestra aplicación sin castigar al motor de base de datos que es un recurso mas critico, imaginemos una query
con Order By innecesario que la usan cientos de usuarios por día
 
Aquí le dejo el código script para que hagan sus pruebas
 

Currently rated 5.0 by 6 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Categories: Tunning
Posted by maccotto on Monday, October 12, 2009 6:09 AM
Permalink | Comments (0) | Post RSSRSS comment feed