Impacto en el rendimiento usando CAST en T-SQL


12

Tenemos un generador de SQL que emite sentencias condicionales de SQL de forma genérica para campos específicos (que en aras de la discusión: etiquetaremos como myField).

Si myFieldes de tipo NVARCHAR, podemos hacer una comparación de dicho campo contra una cadena de este modo: myField = 'foo'.

Sin embargo, esto no funciona para campos de tipo NTEXT. Por lo tanto, tenemos que hacer la comparación con un reparto: CAST(myField as NVARCHAR(MAX)) = 'foo'. De hecho, esto funcionará si myFieldes de tipo NVARCHARo NTEXT.

¿Cuál es el éxito en el rendimiento de hacer el elenco antes mencionado en un campo que ya es de tipo NVARCHAR? Espero que SQL Server sea lo suficientemente inteligente como para reconocer dinámicamente que myFieldya es de tipo NVARCHAR(convirtiendo efectivamente CASTen un no-op).


Una nota rápida para cualquiera que encuentre esta pregunta: NTEXT (y TEXT e IMAGE) están oficialmente en desuso y se deben eliminar en alguna versión futura de SQL Server (aunque IIRC todavía funcionan en SQL1014), por lo que debería usar NVARCHR (MAX) (o VARCHAR (MAX) o VARBINARY (MAX)) en su lugar. Reemplazar la columna NTEXT con una NVARCHAR (MAX) en este caso eliminaría la necesidad del reparto, ya que la comparación se puede hacer directamente con ese tipo, y hay otras posibles ganancias de eficiencia aquí y en otros lugares también. Desafortunadamente, no puede indexar una columna * (MAX), pero tampoco puede una TEXT / NTEXT.
David Spillett

Respuestas:


12

Si el reparto de la columna tiene exactamente el mismo tipo de datos y longitud y el predicado de búsqueda es literal, de hecho parece ignorarlo o tratarlo como un no-op y hace un índice de búsqueda de igualdad.

Seek Keys[1]: Prefix: [tempdb].[dbo].[#test].name = Scalar Operator(N'rpc')

Si el reparto de la columna es del mismo tipo de datos pero de mayor longitud y el predicado de búsqueda es un literal de cadena, provoca una exploración de índice. Obviamente, esto debe evitarse.

Si el reparto de la columna tiene el mismo tipo de datos y la misma longitud o mayor y el predicado de búsqueda es una variable local, agrega un operador escalar de cálculo al plan de ejecución. Esto llama GetRangeThroughConverty genera un rango.

Este rango se usa para hacer una búsqueda de índice y parece bastante eficiente

Seek Keys[1]: 
Start: [tempdb].[dbo].[#test].name > Scalar Operator([Expr1006]), 
End: [tempdb].[dbo].[#test].name < Scalar Operator([Expr1007])

Código de prueba

SELECT *
 INTO #test
  FROM [master].[dbo].[spt_values]

CREATE NONCLUSTERED INDEX [ixname] ON #test
(
    [name] ASC
)

DECLARE @name NVARCHAR(MAX)

SET @name = 'rpc'

SELECT name
FROM #test
WHERE CAST(name AS NVARCHAR(35))= @name --Cast the same and local variable

SELECT name
FROM #test
WHERE CAST(name AS NVARCHAR(MAX))=@name --Cast to longer and local variable

SELECT name
FROM #test
WHERE CAST(name AS NVARCHAR(35))='rpc' --Cast the same and literal

SELECT name
FROM #test
WHERE CAST(name AS NVARCHAR(MAX))='rpc' --Cast to longer and literal

6

En general, CASTmatará el rendimiento porque invalida cualquier uso de búsquedas de índice como muestra el último ejemplo de Martin Smith. La nvarchar(max)transmisión a una longitud diferente significa un tipo de datos diferente: el hecho de que todo nvarcharsea ​​irrelevante.

Además de eso, el tipo de datos del lado derecho de la comparación también importa. Si se trata de una variable local o parámetro de una longitud diferente, un lado estará implícitamente CASTen el más amplio de los 2 tipos de datos (ver precedencia de tipos de datos ).

Básicamente, si usted tiene un general CASTque nvarchar(max)se bollix cosas. Consideraría arreglar el uso de ntextantes de agregar CASTtodo.

La conversión puede no mostrarse en el plan de consulta. Ver el artículo del blog de Paul White


2

Solo una nota, Casting como este donde Datecreated es datetime

 Cast (Datecreated as date) = cast(@MydatetimeValue as date)

No interrumpe la capacidad de SQL para usar índices si los índices existen, y si no existen, puede resultar en el registro de un índice faltante.

De manera similar, cuando se convierte desde inthacia tinyinto biginthacia int, etc., la función de conversión no impide que SQL use índices SI el optimizador sabe que la operación de conversión no cambia el orden de clasificación de los 2 tipos de datos comparables.

Aquí hay un montón de pruebas que puede ejecutar y ver el plan real usando Adventureworks2008R2

select count(*) from Sales.SalesOrderDetail where SalesOrderID = 8 --1
select top 10 * from Sales.SalesOrderDetail where cast(SalesOrderID as tinyint) = 8  --2
select top 10 * from Sales.SalesOrderDetail where cast(SalesOrderID as bigint) = 8  --3
select top 10 SalesOrderID from Sales.SalesOrderDetail where cast(ModifiedDate  as date) = '19780322' --4
select top 10 SalesOrderID from Sales.SalesOrderDetail where convert(date,ModifiedDate) = '19780322'  --5
select top 10 SalesOrderID from Sales.SalesOrderDetail where cast(ModifiedDate as varchar(20)) = '1978'  --6 -- THIS WILL NOT USE INDEX
select  SalesOrderID from Sales.SalesOrderDetail where cast(ModifiedDate  as date) between '19780101' and '19780109'  --7

1
emitir como fecha puede hacer una búsqueda de índice, pero aún tiene problemas en comparación con la expresión como una búsqueda de rango sin un elenco.
Martin Smith
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.