SQL Server, ideas y experiencias

Materiales-Índices y rendimiento (Performance) en el SQL Server

por Jose Mariano Alvarez 3. noviembre 2009

El objetivo fue aprender como el SQL Server almacena los datos y como utiliza los índices para poder diseñarlos y usarlos eficientemente. El objetivo final fue que los asistentes puedan tener el conocimiento necesario para optimizar mediante índices el uso que hace una aplicación de los recursos disponibles en el SQL Server.

Los siguientes enlaces tienen el contenido de los materiales mostrados en el curso dictado en el MUG y que no están incluidos en el CD con materiales que han recibido.

Índices y rendimiento (Performance) en el SQL Server.pdf (1,76 mb)

DemosIndicesOct2009.zip (25,81 kb)

Tags: , , , , ,

Eventos y conferencias

La alineación de particiones de disco y el SQL Server – Documento de SQLCAT

por Jose Mariano Alvarez 27. agosto 2009

La alineación de particiones de disco es una técnica esencial pero a menudo se la pasa por alto. Es una de las herramientas posibles que podemos usar para mejorar el rendimiento o performance del SQL Server 2008. Configurar el rendimiento óptimo del disco muchas veces es visto como un arte en lugar de como una ciencia. Una de las buenas prácticas para configurar un rendimiento óptimo es la alineación de particiones de disco que más que arte tiene ciertas reglas básicas a seguir.

Windows Server 2008 intenta ajustar automáticamente las nuevas particiones, sin embargo como no corrige la configuración de las particiones ya existentes, en las particiones creadas en versiones anteriores de Windows la alineación de particiones de disco sigue siendo una tecnología relevante a tener en cuenta aun en Windows Server 2008.

Este paper que recientemente ha publicado el grupo SQLCAT, trata de explicar y comparar el desempeño de los almacenamientos alineados y no alineados y de explicar por qué las particiones no alineadas puede afectar negativamente rendimiento I/O.

Algunas de las mediciones que se encuentran en el documento muestran por ejemplo, casos de mejoras del 30% en la latencia y duración en los discos o como seis discos alineados pueden tener mejor rendimiento que ocho discos no alineados.

También describe cómo Windows Server 2008 intenta solucionar los problemas relacionados con la alineación de particiones para las particiones nuevas y explica cómo es la alineación de particiones de disco para los almacenamientos en Windows Server 2003, incluyendo el análisis, el diagnóstico y los planes de remediación.

Los siguientes temas también se explican:

  • Información de contexto relacionada
  • Implementación
  • Consideraciones a tener en cuenta de los proveedores de almacenamiento
  • Desplazamientos de particiones de arranque validos
  • El protocolo (simple) de alineación de particiones
  • Como definir el tamaño de unidad de asignación de archivos
  • Links a más información

Para aquellos que quieran leerlo les dejo el Link:

http://msdn.microsoft.com/en-us/library/dd758814.aspx

Tags: , ,

Documentos

Usando el Resource Governor - Documento

por Jose Mariano Alvarez 25. julio 2009

CPUPerfGraf Entre las nuevas características que se encuentran en el SQL Server 2008 está el Resource Governor (regulador de recursos), que ofrece la capacidad de vigilar y controlar el uso de CPU y memoria.

Este “White Paper” explica varios escenarios de uso práctico del Resource Governor y ofrece orientación sobre las mejores prácticas para emplearlo de manera eficiente. Se encuentra dirigido a los profesionales de IT y administradores de base de datos responsables de la gestión de los servidores SQL Server 2008. Entre las características que cubre el documento se encuentran la forma de utilizar el Resource Governor tomando en cuenta los requerimientos de concurrencia, de alta disponibilidad, la consolidación, la virtualización, o los acuerdos de nivel de servicio (Service Level Agreement o SLA) para los casos de entornos de producción con SQL Server.

En el documento se destacan varios escenarios de uso común, que pueden ayudar a decidir cuándo y dónde utilizar esta tecnología, y un resumen de las mejores prácticas cuando se utiliza el Resource Governor.

Les dejo el enlace para poder descargar el documento (en inglés) desde el sitio de Microsoft

Using the Resource Governor (DOCX)

Si les interesan los conceptos básicos les dejo un par de links a los libros de ayuda:

Introducción al regulador de recursos

Conceptos del regulador de recursos

Tags: , , , , , ,

Documentos

Como numerar eficientemente las filas de una consulta select

por Jose Mariano Alvarez 10. julio 2009

Uno de las preguntas más frecuentes es cómo numerar la filas de una consulta. En SQL Server 2005 se introdujo ROW_NUMBER() para poder realizar esta operación fácilmente. Sin embargo en los casos en que no la podemos usar como en SQL Server 2000 existen creencias de que los subquery pueden ser alternativas eficientes y las consecuencias suelen ser catastróficas luego de que el volumen de registros crece.

Usando la tabla AdventureWorks2008.Sales.Customer vamos a probar cuatro estrategias y sus consecuencias.

 

Usando un subquery como habitualmente se sugiere en SQL Server 2000

Select  
    (
        select
            COUNT(*) + 1
        from AdventureWorks2008.Sales.Customer B 
        where b.AccountNumber  < a.AccountNumber
    ) as fila, 
    a.*
from AdventureWorks2008.Sales.Customer a
order by rowguid 

El resultado del plan muestra como se anida un SCAN de la tabla dentro de un NESTED LOOP lo cual genera un severo problema de rendimiento.

  |--Compute Scalar(DEFINE:([Expr1006]=[Expr1004]+(1)))
       |--Nested Loops(Inner Join, OUTER REFERENCES:([a].[CustomerID]))
            |--Compute Scalar(DEFINE:([a].[AccountNumber]=[AdventureWorks2008].[Sales].[Customer].[AccountNumber] as [a].[AccountNumber]))
            |    |--Compute Scalar(DEFINE:([a].[AccountNumber]=isnull('AW'+[AdventureWorks2008].[dbo].[ufnLeadingZeros]([AdventureWorks2008].[Sales].[Customer].[CustomerID] as [a].[CustomerID]),'')))
            |         |--Sort(ORDER BY:([a].[rowguid] ASC))
            |              |--Clustered Index Scan(OBJECT:([AdventureWorks2008].[Sales].[Customer].[PK_Customer_CustomerID] AS [a]))
            |--Compute Scalar(DEFINE:([Expr1004]=CONVERT_IMPLICIT(int,[Expr1010],0)))
                 |--Stream Aggregate(DEFINE:([Expr1010]=Count(*)))
                      |--Index Spool(SEEK:([B].[AccountNumber] < isnull('AW'+[AdventureWorks2008].[dbo].[ufnLeadingZeros]([AdventureWorks2008].[Sales].[Customer].[CustomerID] as [a].[CustomerID]),'')))
                           |--Compute Scalar(DEFINE:([B].[AccountNumber]=isnull('AW'+[AdventureWorks2008].[dbo].[ufnLeadingZeros]([AdventureWorks2008].[Sales].[Customer].[CustomerID] as [B].[CustomerID]),'')))
                                |--Index Scan(OBJECT:([AdventureWorks2008].[Sales].[Customer].[IX_Customer_TerritoryID] AS [B]))

 

Si vemos a continuación los detalles de la cantidad de IO podemos ver claramente como la cantidad de lecturas lógicas es de 1103320,

Table 'Worktable'.
    Scan count 19820,
    logical reads 1103320,
    physical reads 0,
    read-ahead reads 0,
    lob logical reads 0,
    lob physical reads 0,
    lob read-ahead reads 0.


Table 'Customer'.
    Scan count 2,
    logical reads 160,
    physical reads 0,
    read-ahead reads 0,
    lob logical reads 0,
    lob physical reads 0,
    lob read-ahead reads 0.

El resultado de la consulta tardo 1 minuto y 37 segundos

 

 

Usando la nueva sintaxis ROW_NUMBER() introducida en SQL Server 2005

Select
       ROW_NUMBER() OVER (ORDER BY a.AccountNumber ) as fila,
       A.* 
from 
       AdventureWorks2008.Sales.Customer a
order by 
       rowguid 

El resultado del plan muestra no se anida en este caso ningún SCAN sino que es un plan secuancial y muy eficiente.

  |--Sort(ORDER BY:([a].[rowguid] ASC))
       |--Sequence Project(DEFINE:([Expr1002]=row_number))
            |--Segment
                 |--Compute Scalar(DEFINE:([a].[AccountNumber]=[AdventureWorks2008].[Sales].[Customer].[AccountNumber] as [a].[AccountNumber]))
                      |--Sort(ORDER BY:([a].[AccountNumber] ASC))
                           |--Compute Scalar(DEFINE:([a].[AccountNumber]=isnull('AW'+[AdventureWorks2008].[dbo].[ufnLeadingZeros]([AdventureWorks2008].[Sales].[Customer].[CustomerID] as [a].[CustomerID]),'')))
                                |--Clustered Index Scan(OBJECT:([AdventureWorks2008].[Sales].[Customer].[PK_Customer_CustomerID] AS [a]))

 

Vemos como el resultado de aplicar la nueva función ha cambiado y solo se realizan 123 lecturas lógicas.

Table 'Customer'.
    Scan count 1,
    logical reads 123,
    physical reads 0,
    read-ahead reads 0,
    lob logical reads 0,
    lob physical reads 0,
    lob read-ahead reads 0.

 El resultado de la consulta tardo 1 segundo.

 

 

Usando la función IDENTITY() como en SQL Server 2000

En este caso se debe recurrir a una tabla temporal pero vemos que el beneficio es sustancial.

Debido a lo que ocurre con la función IDENTITY() y el ORDER BY se debe crear una tabla temporal para insertar los registros. Les dejo el Enlace a “The behavior of the IDENTITY function when used with SELECT INTO or INSERT .. SELECT queries that contain an ORDER BY clause”.

http://support.microsoft.com/kb/273586

NOTA: Quiero agradecer a Alejandro Mesa quien me hizo el comentario del error en los foros respecto de este comportamiento que yo había omitido en el Post el cual ya se encuentra actualizado y corregido según la documentacióní.

Creamos la tabla temporal para ordenar los registros

CREATE TABLE [dbo].[#Clientes](
    [fila] [int] IDENTITY(1,1) NOT NULL,
    [CustomerID] [int] NULL,
    [PersonID] [int] NULL,
    [StoreID] [int] NULL,
    [TerritoryID] [int] NULL,
    [AccountNumber] [varchar](10) NOT NULL,
    [rowguid] [uniqueidentifier] NOT NULL,
    [ModifiedDate] [datetime] NOT NULL
) ON [PRIMARY]
GO

 

Esta es la parte de la consulta donde se generan los números de fila.

Insert into #Clientes
(
    CustomerID, 
    PersonID, 
    StoreID, 
    TerritoryID, 
    AccountNumber, 
    rowguid, 
    ModifiedDate
)
Select     
    cast(CustomerID as int) as CustomerID , 
    PersonID, 
    StoreID, 
    TerritoryID, 
    AccountNumber, 
    rowguid, 
    ModifiedDate
from AdventureWorks2008.Sales.Customer a
ORDER BY a.AccountNumber 

Ahora vemos nuevamente que no se anida un SCAN en el plan por lo que resulta mucho más eficiente que en el caso del subquery.

  |--Table Insert(OBJECT:([#Clientes]), SET:([#Clientes].[CustomerID] = [Expr1006],[#Clientes].[PersonID] = [AdventureWorks2008].[Sales].[Customer].[PersonID] as [a].[PersonID],[#Clientes].[StoreID] = [AdventureWorks2008].[Sales].[Customer].[StoreID] as [a
       |--Compute Scalar(DEFINE:([Expr1007]=getidentity((-7),(0),N'#Clientes')))
            |--Top(ROWCOUNT est 0)
                 |--Compute Scalar(DEFINE:([a].[AccountNumber]=[AdventureWorks2008].[Sales].[Customer].[AccountNumber] as [a].[AccountNumber], [Expr1006]=CONVERT(int,[AdventureWorks2008].[Sales].[Customer].[CustomerID] as [a].[CustomerID],0)))
                      |--Sort(ORDER BY:([a].[AccountNumber] ASC))
                           |--Compute Scalar(DEFINE:([a].[AccountNumber]=isnull('AW'+[AdventureWorks2008].[dbo].[ufnLeadingZeros]([AdventureWorks2008].[Sales].[Customer].[CustomerID] as [a].[CustomerID]),'')))
                                |--Clustered Index Scan(OBJECT:([AdventureWorks2008].[Sales].[Customer].[PK_Customer_CustomerID] AS [a]))

 

Si vemos los resultados en I/O solo se realizan 123 lecturas logicas

Table 'Customer'.
    Scan count 1,
    logical reads 123,
    physical reads 0,
    read-ahead reads 0,
    lob logical reads 0,
    lob physical reads 0,
    lob read-ahead reads 0.

Select * 
from #Clientes c
order by C.rowguid 

El plan de esta parte es el SCAN y el SORT.

|--Sort(ORDER BY:([c].[rowguid] ASC))
       |--Table Scan(OBJECT:([tempdb].[dbo].[#Clientes] AS [c]))

 

Si vemos los resultados en I/O solo se realizan 167 lecturas logicas

Table '#Clientes_000000000007'.
    Scan count 1,
    logical reads 167,
    physical reads 0,
    read-ahead reads 0,
    lob logical reads 0,
    lob physical reads 0,
    lob read-ahead reads 0.

Queda eliminar la tabla temporal con

drop table #Clientes 

El total acumulado de tiempo fue de 1 segundo.

 

 

Conclusión:

 

Caso Lecturas Tiempo
Subquery 1103480 97 seg
Row_Number() 123 1 seg
Tabla temporal + Identity() 280 1 seg

Vemos que es mucho más eficiente la tercera opción usando una tabla temporal y la función identiy() que la opción de la subquery (subconsulta). Sin embargo esta no es  tan eficiente como en el caso de la nueva sintaxis de SQL Server 2005/8 con ROW_NUMBER(). Por lo tanto si tiene SQL Server 2005 o SQL Server 2008 Lo mejor es usar la funcion ROW_NUMBER() y si tienen SQL Server 2000 la mejor opción es usar la tabla temporal y la función Identity().

 

En todos los casos todas las lecturas fueron lógicas ya que la tabla tenia todas las paginas en memoria.

NOTA: Me falta comparar la alternativa de usar CLR y de usar un cursor de T-Sql.

Tags: , , ,

Artículos

Buenas prácticas en el uso de índices - La selectividad

por Jose Mariano Alvarez 16. mayo 2009

Introducción

Los índices mejoran el rendimiento de las consultas que seleccionan un pequeño porcentaje de las filas de una tabla. La selectividad de los índices es uno de los conceptos más importantes a la hora de entender si un índice puede ser útil en esos casos. Para comprender el concepto, vamos a analizar las consecuencias de la selectividad de un índice no agrupado (NON CLUSTERED) sobre una columna de una tabla de la base de datos de ejemplo AdventureWorks que tiene el SQL Server 2008. Los accesos puntuales, que es lo que vamos a analizar en este caso, no son la única utilidad que nos ofrecen los índices para mejorar los costos, pero es importante comprender por qué en ocasiones no se utilizan índices existentes.

La motivación

Uno de las características fundamentales del SQL Server 2008 (y versiones anteriores también) es que la selección de las estrategias de ejecución del motor relacional se encuentran basadas en los costos. De esta manera cobra vital importancia la creación de índices para reducir estos costos.

Cuando creamos índices una de las posibles utilidades que ofrecen es reducir la carga impuesta a la base de datos permitiendo accesos puntuales a los registros a partir del índice lo que habitualmente se conoce como INDEX SEEK, para luego acceder en un segundo paso a los registros de la tabla. Si no existe el índice, en lugar de que el motor de la base de datos pueda acceder puntualmente, tiene que explorar todos los registros de la tabla o índice agrupado, lo que se conoce como TABLE SCAN o CLUSTER INDEX SCAN.

Ambas estrategias de acceso tienen sus costos. Por lo tanto la selección que hace el motor de la base de datos deberá estar de acuerdo a los costos. Además, cada una de las estrategias tiene sus consecuencias.

Si tenemos índices que no se utilizan simplemente estaríamos aumentando el trabajo que tiene que realizar el SQL Server para mantener actualizados los índices cuando se realizan modificaciones y estaríamos aumentando el espacio de almacenamiento usado por guardar las estructuras de los índices tanto en memoria como en disco. Por lo tanto es importante entender algunos conceptos importantes para crear buenos índices.

La selectividad

Vamos a definir la selectividad de un índice, como el porcentaje promedio de filas en una tabla que tienen el mismo valor en las columnas involucradas en el índice, esto equivale al cociente (ratio) entre el número de valores distintos en las columnas que definen el índice y el número de registros en la tabla. La selectividad ideal es 1 y representa el caso en que todas las entradas del índice son únicas. Esta selectividad puede ser garantizada sólo por índices únicos. SQL Server crea automáticamente índices en las columnas de todas las restricciones de integridad de clave primaria (PRIMARY KEY) y únicas (UNIQUE) que de definen. Esto ocurre porque generalmente estos índices son los más selectivos y más eficientes para optimizar el rendimiento ya que permiten seleccionar al motor de la base de datos estrategias particulares.

Ecuacion densidad

Quizá sea más simple comprender el inverso multiplicativo de la selectividad que representa el promedio de filas de igual valor que se van a obtener en el caso de que exista un condición por igualdad en la clausula WHERE de la sentencia.

ecuacion promedio de valores iguales

Veamos un simple ejemplo en la base de datos AdventureWorks de nuestro SQL Server 2008.

USE AdventureWorks

GO

SELECT COUNT (DISTINCT ProductID) FROM Sales.SalesOrderDetail

266

SELECT COUNT(*) FROM Sales.SalesOrderDetail

121317

Por lo tanto vemos que la selectividad de nuestro índice por la columna ProductID es 266/121317 0.002192602 lo que representa una selectividad regular ya que nuestro ideal es 1. Si vemos el inverso multiplicativo, lo que equivale a hacer121317/266 obtenemos 456.07 que representa el número promedio de registros cuando buscamos las filas de un producto en nuestra tabla de detalle de órdenes.

Veamos las consecuencias a la hora de realizar consultas.

Ejemplo 1

SELECT * FROM Sales.SalesOrderDetail WHERE productID = 870

El plan resultante es el siguiente

Selectividad1

Como podemos ver el plan refleja el hecho de que existen muchos registros con el productID = 870 y por lo tanto le resulta más eficiente realizar en SCAN. La selectividad de la condición no es adecuada para un acceso puntual.

Ejemplo 2

SELECT * FROM Sales.SalesOrderDetail WHERE productID = 719

El plan resultante es el siguiente

Selectividad2

En este caso el SQL Server elige realizar un SEEK del índice NON CLUSTERED porque determina que es más eficiente. Luego debe realizar un KEY LOOKUP para obtener el resto de las columnas que no se encuentran en el índice. Si comparamos estos dos casos podemos ver claramente que a diferentes valores de productID, el SQL Server determina en cual es el mejor método de acceso analizando la selectividad de cada valor de productID en particular.

Ejemplo 3

DECLARE @ProdId INT

SET @ProdId = 719

SELECT * FROM Sales.SalesOrderDetail WHERE productID = @ProdId

El plan resultante es el siguiente

Selectividad3

En este ejemplo vemos que a pesar de que el valor es el mismo del ejemplo anterior, debido al uso de la variable no puede conocer el valor específico al momento de compilar y optimizar, y por lo tanto utiliza el caso promedio que corresponde a la selectividad promedio y por lo tanto realiza un SCAN independientemente del valor de la variable.

Vamos a dejar para otro artículo el tema de los parámetros de los procedimientos almacenados ya que requiere un análisis particular.

La densidad

Si buscamos en la documentación del SQL Server encontramos que el comando DBCC SHOW_STATISTICS nos permite ver la información estadística usada para seleccionar el plan de ejecución. Dentro de esta información se encuentran las densidades y un histograma disponible para la primera columna del índice. Esta fue la información que permitió determinar los planes de ejecución en los ejemplos anteriores.

La densidad se define como:

Ecuacion Densidad

Por lo tanto si multiplicamos la densidad por la cantidad de registros de la tabla obtenemos el promedio de valores iguales o el inverso multiplicativo de la selectividad.

DBCC SHOW_STATISTICS ( 'Sales.SalesOrderDetail' , 'IX_SalesOrderDetail_ProductID')

Dentro de la información que nos presenta está:

 

All density

Average Length

Columns

0,003759399

4

ProductID

8,242868E-06

8

ProductID, SalesOrderID

8,242868E-06

12

ProductID, SalesOrderID, SalesOrderDetailID

Si multiplicamos por la cantidad de filas de la tabla obtenemos 0, 003759399 * 121317 = 456,07 que es el mismo valor que antes.

Conclusión

Un índice puede ser selectivo de acuerdo al valor utilizado. Si el índice no es selectivo, el SQL Server no lo va a utilizar para acceder a los registros mediante un SEEK y va a preferir realizar un TABLE SCAN. No tiene sentido crear índices para acceder puntualmente a un subconjunto de registros de la tabla en el caso de no ser selectivos. Existen otros motivos para crear índices como cover index que justifican en algunos casos índices no tan selectivos.

Tags: , , ,

Artículos

Implementiando “SQL Server Fast Track Data Warehouse”

por Jose Mariano Alvarez 7. marzo 2009

El paper define un modelo de configuración de referencia conocido como “SQL Server Fast Track Data Warehouse” y una aproximación para un multiprocesador simétrico basado en las expectativas de rendimiento y escalabilidad para el tratamiento de cargas de trabajo de I/O secuencial.

El propósito del documento es definir un enfoque arquitectónico repetible para implementar un modelo escalable de data warehouse en SQL Server 2008 en un multiprocesador simétrico (SMP).

El resultado final de este proceso representa el mínimo recomendado e incluye todo el software y el hardware necesarios para alcanzar y mantener un nivel básico de rendimiento escalable tomando en cuenta la carga secuencial de E/S clásica de escenarios de data warehouse en lugar de la carga de trabajo tradicional de E/S al azar de los OLTP.

mplementing a SQL Server Fast Track Data Warehouse

Tags: , , , ,

Artículos | Documentos

Los índices y las condiciones de búsqueda

por Jose Mariano Alvarez 24. febrero 2009

Para poder entender lo que hace de SQL Server antes de aplicar cualquier tipo de índice en una consulta tenemos que entender que el SQL Server tiene que determinar si un determinado índice es útil o no. Este articulo trata de mostrar como un predicado mal escrito impide el uso de accesos puntuales mediante INDEX SEEK.

Para entender esto vamos a simplificar las cosas y comenzar con una consulta trivial.

SELECT <Columnas devueltas>
FROM   <Tabla>
WHERE  <Condición>

Está simple consulta, que utiliza una sola tabla, precisa de las “columnas devueltas” y de las columnas utilizadas en la condición para verificar si la cumplen. Por el momento consideraremos condiciones simples sin subconsultas.

El punto crítico aquí es la condición, ya que es esta la que limita la cantidad de filas devueltas y por lo tanto la posibilidad de hacer un acceso puntual (SEEK) mediante índices o una exploración (SCAN) ya sea de índices o de tabla dependiendo de las columnas involucradas. El problema radica en que el SQL Server debe determinar conociendo información estadística de los datos de las columnas involucradas en la condición, la estructura de los índices disponibles y la forma en que está escrita la condición si el uso de un índice mediante acceso puntual (SEEK) es adecuado o es preferible una exploración (SCAN).

Vamos a ignorar por el momento las columnas devueltas y el tipo de índice (que dejaremos para otros artículos ) a efectos de simplificar el análisis y entender la idea de los argumentos de búsqueda,. Simplemente nos vamos a concentrar en como un predicado de búsqueda mal expresado puede impedir el uso de los índices mediante un SEEK.

Veamos un ejemplo usando AdventureWorks:

USE AdventureWorks
GO

SELECT * 
FROM Sales.SalesOrderHeader 
WHERE customerid = 117

 

IndexSeek

Aquí vemos como el predicado de búsqueda permite determinar facilmente que el acceso puntual por índice es útil. Puede verse el Index Seek. Esto ocurre porque el predicado customerid = 117 es un buen argumento de búsqueda y por lo tanto dado que tiene información estadística puede determinar que le conviene usar el índice porque determina que devuelve un porcentaje muy bajo de filas en proporción a las que tiene la tabla.

Qué ocurre si cambiamos el argumento de búsqueda:

SELECT * 
FROM Sales.SalesOrderHeader 
WHERE customerid + 1 = 118

 

IndexScan

En este caso el resultado es el mismo, en cuanto a filas devueltas, ya que ambos predicados son equivalentes, pero la condición customerid + 1 = 118 no permite el acceso puntual mediante SEEK y debe hacer un INDEX SCAN.

Si analizamos un poco ambas condiciones vemos que la segunda condición por igualdad esta aplicada al resultado de una operación suma y no a una columna por lo que no le es posible al motor del SQL Server hacer una búsqueda puntual porque lo que está en el índice no es el resultado de la suma sino el customerid. Por lo tanto no le queda más remedio que revisar fila por fila para determinar si se cumple la condición de igualdad.

Para verlo más claramente es muy parecido a si buscamos a todas las Daniela de la guía telefónica. Dado que esta ordenada por apellido y luego por nombre no es posible hacer un acceso puntual a las páginas donde se encuentran y es necesario revisar todas las páginas de la guía verificando abonado telefónico por abonado telefónico si se cumple la condición. Esto mismo ocurriría aunque hubiera solo 10 personas con nombre Daniela.

Conclusión

Si no escribimos las condiciones de nuestras consultas de manera adecuada nunca podrá usar los índices para realizar accesos puntuales (INDEX SEEK). En líneas generales las condiciones requieren de un predicado del tipo columna <operador> <expresión>, donde no todos los operadores cumplen con esta condición.

Tags: , ,

Artículos

Powered by SQL Total Consulting


View Jose Mariano Alvarez's profile on LinkedIn

 Add to Technorati Favorites 

Calendar

<<  octubre 2017  >>
lumamijuvido
2526272829301
2345678
9101112131415
16171819202122
23242526272829
303112345

View posts in large calendar

Locations of visitors to this page

Widget Twitter not found.

Root element is missing.X


Valid XHTML 1.0 Transitional

Valid CSS!