Cuando trabajamos con SQL Server, es importante tener en cuenta los posibles problemas que pueden surgir debido a las diferencias en los tipos de datos. En este artículo, exploraremos un problema específico que puede ocurrir al utilizar vistas, uniones y diferentes tipos de datos en SQL Server.
Recientemente, me encontré con un problema de rendimiento que tomó algún tiempo diagnosticar. El problema estaba relacionado con una vista que seleccionaba datos de múltiples tablas de archivo y utilizaba uniones para combinar los conjuntos de resultados. Esta vista había estado en producción durante varios años sin ningún problema, pero de repente comenzó a agotar el tiempo de espera.
Después de una semana de investigación, que incluyó modificaciones de índices y actualizaciones de estadísticas, descubrí que la causa raíz del problema era una diferencia en el tipo de datos de una columna entre la tabla actual y las tablas de archivo. Vamos a profundizar en los detalles de este problema y su solución.
Primero, creemos dos tablas llamadas “agentes” y “agentes2009” con esquemas similares. La única diferencia es la longitud de la columna “numerodecuenta”. La tabla “agentes2009” tiene una longitud de 9, mientras que la tabla “agentes” tiene una longitud de 10.
CREATE TABLE [dbo].[agentes2009](
[numerodecuenta] [char](9) NULL
) ON [PRIMARY]
CREATE TABLE [dbo].[agentes](
[numerodecuenta] [char](10) NULL
) ON [PRIMARY>
A continuación, creamos una vista llamada “vw_agentes” que selecciona datos de ambas tablas utilizando una unión:
CREATE VIEW [dbo].[vw_agentes]
AS
SELECT numerodecuenta FROM agentes2009
UNION ALL
SELECT numerodecuenta FROM agentes
Ahora, insertemos un millón de filas en cada tabla utilizando números de cuenta generados al azar:
DECLARE @Superior INT;
DECLARE @Inferior INT;
SET @Inferior = 65 ---- El número aleatorio más bajo es la A mayúscula
SET @Superior = 90 ---- El número aleatorio más alto es la Z mayúscula
SELECT CHAR(ROUND(((@Superior - @Inferior -1) * RAND() + @Inferior), 0))
+CHAR(ROUND(((@Superior - @Inferior -1) * RAND() + @Inferior), 0))
+CHAR(ROUND(((@Superior - @Inferior -1) * RAND() + @Inferior), 0))
+CAST(ABS(CHECKSUM(NEWID())) % 900000 + 100000 AS CHAR(6))
Después de insertar los datos, podemos ejecutar una declaración select en la vista y ver el plan de ejecución:
SELECT numerodecuenta FROM vw_agentes
WHERE numerodecuenta = 'QOT039365' OPTION(maxdop 1)
En el plan de ejecución, podemos ver que cada tabla se lee utilizando un escaneo de tabla. Esto es esperado ya que ninguna de las tablas tiene un índice. Sin embargo, se necesita un cálculo escalar para convertir el tipo de datos “char(9)” a “char(10)” antes de la unión.
Para mejorar el rendimiento, podemos crear índices en ambas tablas:
CREATE NONCLUSTERED INDEX [IX_agentes2009] ON [dbo].[agentes2009]
(
[numerodecuenta] ASC
) ON [PRIMARY]
CREATE NONCLUSTERED INDEX [IX_agentes] ON [dbo].[agentes]
(
[numerodecuenta] ASC
) ON [PRIMARY]
Al ejecutar nuevamente la declaración select, podemos ver que se realiza una búsqueda de índice en la tabla “agentes”, pero aún se utiliza un escaneo de tabla en la tabla “agentes2009”. Este comportamiento es causado por la diferencia en la longitud de la columna “numerodecuenta” entre las dos tablas.
Para resolver este problema, debemos alterar la columna “numerodecuenta” en la tabla “agentes2009” para que coincida con la longitud de la tabla “agentes”. Esto requiere eliminar y recrear el índice que hace referencia a la columna:
DROP INDEX dbo.agentes2009.IX_agentes2009
go
ALTER TABLE dbo.agentes2009 ALTER COLUMN numerodecuenta CHAR(10)
GO
CREATE NONCLUSTERED INDEX [IX_agentes2009] ON [dbo].[agentes2009]
(
[numerodecuenta] ASC
) ON [PRIMARY]
Finalmente, cuando ejecutamos nuevamente la declaración select original, podemos ver que se utiliza una operación de búsqueda de índice en ambas tablas y se ha eliminado el cálculo escalar ya que todas las columnas son del mismo tipo y longitud.
Este problema y su solución resaltan la importancia de ser consciente de las diferencias en los tipos de datos al trabajar con SQL Server. Si bien SQL Server puede manejar columnas convertidas implícitamente en declaraciones de unión, el plan de consulta resultante no siempre será el esperado.
Al comprender estos conceptos y tener en cuenta las diferencias en los tipos de datos, puede optimizar sus consultas de SQL Server y evitar posibles problemas de rendimiento.