Recientemente he discutido algunas características que Microsoft desaconseja usar, y que creo que también deberías olvidar que existen. Hubo un caso en el que un colega promovía constantemente la vista de compatibilidad con versiones anteriores en desuso sys.sysprocesses
en lugar de vistas de administración dinámica (DMV) más nuevas, y otro caso en el que un colega diferente eliminó un servidor de producción usando SQL Server Profiler.
Mi último encuentro con cosas que es mejor olvidar es un nuevo procedimiento almacenado con un ntext
parámetro. Revisé y, efectivamente, el tipo de datos coincide con el esquema de la tabla subyacente. Mi mente comenzó a pensar en estos tipos de datos más antiguos para explicar por qué ya no deberíamos usarlos:
- imagen
- ntext
- texto
Estos tipos están en la lista obsoleta por muchas razones y han ocupado un lugar permanente en esa lista desde que fueron reemplazados por el max
tipos en SQL Server 2005. Algunos de estos puntos débiles incluyen:
- no puede usar muchas funciones de cadena, como
LEFT()
,RTRIM()
,UPPER()
, y la mayoría de los operadores de comparación; - necesitas usar funciones como
TEXTPTR
,WRITETEXT
yUPDATETEXT
para modificaciones; - no puede usar los tipos como variables locales;
- no puede hacer referencia a las columnas en
DISTINCT
,GROUP BY
,ORDER BY
, o como una columna incluida (no es que desee hacer nada de esto); - los valores más pequeños que podrían caber en la fila solo pueden hacerlo con el
text in row
opción.
Esa no es una lista exhaustiva; hay otras diferencias que podrías considerar más o menos importantes. La razón más apremiante para mí es que no puede reconstruir el índice agrupado en línea si la tabla contiene uno de estos tipos de datos.
Vamos a crear una base de datos simple con algunas tablas:
CREATE DATABASE BadIdeas; GO USE BadIdeas; GO CREATE TABLE dbo.t1(id bigint IDENTITY PRIMARY KEY, msg nvarchar(max)); CREATE TABLE dbo.t2(id bigint IDENTITY PRIMARY KEY, msg ntext);
Ahora, intentemos realizar operaciones en línea en las tablas:
ALTER TABLE dbo.t1 REBUILD WITH (ONLINE = ON); GO ALTER TABLE dbo.t2 REBUILD WITH (ONLINE = ON);
Si bien la primera declaración tiene éxito, la segunda genera un mensaje de error como este:
Mensaje 2725, nivel 16, estado 2No se puede realizar una operación en línea para el índice 'PK__t2__3213E83FEEA1E0AD' porque el índice contiene la columna 'msg' del tipo de datos texto, ntext, imagen o FILESTREAM. Para un índice no agrupado, la columna podría ser una columna de inclusión del índice. Para un índice agrupado, la columna podría ser cualquier columna de la tabla. Si se usa DROP_EXISTING, la columna podría ser parte de un índice nuevo o antiguo. La operación debe realizarse fuera de línea.
En un escenario único, el mensaje de error es bastante fácil de manejar:o se salta la tabla o, si es absolutamente necesario reconstruir la tabla, trabaja con sus equipos para programar una interrupción. Cuando está automatizando una solución de mantenimiento de índices o implementando nuevas configuraciones de compresión en su entorno, es un poco más molesto hacer que su solución maneje el estado presente o futuro.
¿Qué hacer?
Puede comenzar a reemplazar estas columnas con sus contrapartes más modernas. Aquí hay una consulta para ayudarlo a rastrearlos usando sys.columns
vista de catálogo, pero está solo para cualquier referencia explícita que pueda existir en el código de su aplicación:
SELECT [Schema] = s.name, [Object] = o.name, [Column] = c.name, [Data Type] = TYPE_NAME(c.user_type_id) + CASE WHEN c.system_type_id <> c.user_type_id THEN N' (' + TYPE_NAME(c.system_type_id) + N')' ELSE N'' END FROM sys.columns AS c INNER JOIN sys.objects AS o ON c.[object_id] = o.[object_id] INNER JOIN sys.schemas AS s ON o.[schema_id] = s.[schema_id] WHERE c.system_type_id IN (34, 35, 99) ORDER BY [Schema], [Object], [Column];
Salida:
Puede ser tentador ingresar a SSMS y cambiar manualmente los tipos de datos de estas columnas, pero también puede haber otras implicaciones. Por ejemplo, las columnas pueden tener restricciones predeterminadas asociadas a ellas. Y es posible que tenga procedimientos almacenados con parámetros que deben actualizarse en tándem:
CREATE PROCEDURE dbo.sp1 @p1 ntext AS PRINT 1; GO
Para encontrar todos estos casos, puede adaptar la consulta anterior para buscar en sys.parameters
vista de catálogo en su lugar:
SELECT [Schema] = s.name, [Object] = o.name, [Parameter] = p.name, [Data Type] = TYPE_NAME(p.user_type_id) + CASE WHEN p.system_type_id <> p.user_type_id THEN N' (' + TYPE_NAME(p.system_type_id) + N')' ELSE N'' END FROM sys.objects AS o INNER JOIN sys.schemas AS s ON o.[schema_id] = s.[schema_id] INNER JOIN sys.parameters AS p ON p.[object_id] = o.[object_id] WHERE p.system_type_id IN (34, 35, 99) ORDER BY [Schema], [Object], [Parameter];
Salida:
Si necesita devolver estos datos en todas las bases de datos, puede tomar sp_ineachdb
, un procedimiento que escribí (y documenté aquí y aquí) para superar varias de las limitaciones en el sp_MSforeachdb
con errores, sin documentar y sin soporte . Entonces puedes hacer esto:
EXEC master.dbo.sp_ineachdb @command = N'SELECT [Database] = DB_NAME(), [Schema] = s.name, [Object] = o.name, [Column] = c.name, [Data Type] = TYPE_NAME(c.user_type_id) + CASE WHEN c.system_type_id <> c.user_type_id THEN N'' ('' + TYPE_NAME(c.system_type_id) + N'')'' ELSE N'''' END FROM sys.columns AS c INNER JOIN sys.objects AS o ON c.[object_id] = o.[object_id] INNER JOIN sys.schemas AS s ON o.[schema_id] = s.[schema_id] WHERE c.system_type_id IN (34, 35, 99) ORDER BY [Schema], [Object], [Column]; SELECT [Database] = DB_NAME(), [Schema] = s.name, [Object] = o.name, [Parameter] = p.name, [Data Type] = TYPE_NAME(p.user_type_id) + CASE WHEN p.system_type_id <> p.user_type_id THEN N'' ('' + TYPE_NAME(p.system_type_id) + N'')'' ELSE N'''' END FROM sys.objects AS o INNER JOIN sys.schemas AS s ON o.[schema_id] = s.[schema_id] INNER JOIN sys.parameters AS p ON p.[object_id] = o.[object_id] WHERE p.system_type_id IN (34, 35, 99) ORDER BY [Schema], [Object], [Parameter];';
Una nota al margen interesante aquí:si ejecuta eso en todas las bases de datos, descubrirá que, incluso en SQL Server 2019, Microsoft todavía usa algunos de estos tipos antiguos.
Puede automatizar aún más eso ejecutándolo desde PowerShell o cualquier herramienta de automatización que use para administrar múltiples instancias de SQL Server.
Por supuesto, eso es solo el comienzo:solo produce una lista. Puede ampliarlo aún más para generar una versión preliminar de ALTER TABLE
comandos, necesitaría actualizar todas las tablas, pero esos comandos deberían revisarse antes de ejecutarlos, y aún tendría que modificar los procedimientos usted mismo (generando ALTER PROCEDURE
los comandos que solo tienen esos nombres de tipo de parámetro reemplazados correctamente no es un ejercicio fácil de ninguna manera). Aquí hay un ejemplo que genera ALTER TABLE
comandos, teniendo en cuenta la nulabilidad pero sin otras complicaciones como las restricciones predeterminadas:
SELECT N'ALTER TABLE ' + QUOTENAME(s.name) + N'.' + QUOTENAME(o.name) + N' ALTER COLUMN ' + QUOTENAME(c.name) + N' ' + CASE c.system_type_id WHEN 34 THEN N'varbinary' WHEN 35 THEN N'varchar' WHEN 99 THEN N'nvarchar' END + N'(max)' + CASE c.is_nullable WHEN 0 THEN N' NOT' ELSE N'' END + N' NULL;' FROM sys.columns AS c INNER JOIN sys.objects AS o ON c.[object_id] = o.[object_id] INNER JOIN sys.schemas AS s ON o.[schema_id] = s.[schema_id] WHERE c.system_type_id IN (34, 35, 99);
Salida:
ALTER TABLE [dbo]. [t2] ALTER COLUMN [msg] nvarchar (max) NULL;
Y en caso de que te lo estés preguntando, no, no puedes hacer esta operación única con un WITH (ONLINE = ON)
explícito opción:
La operación ALTER COLUMN en línea no se puede realizar para la tabla 't2' porque la columna 'msg' actualmente tiene o se está modificando en un tipo de datos no compatible:texto, ntext, imagen, tipo CLR o FLUJO DE ARCHIVOS. La operación debe realizarse fuera de línea.
Conclusión
Con suerte, esto proporciona una buena base sobre por qué desea eliminar estos tipos obsoletos y un punto de partida para realizar los cambios. Microsoft aprendió por las malas que no hay mucha funcionalidad que puedan extraer del producto, por lo que no me preocupa que dejen de existir en mi vida. Pero el miedo a la eliminación no debería ser su único motivador.