sql >> Base de Datos >  >> RDS >> Database

Funciones obsoletas para sacar de su caja de herramientas - Parte 3

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 y UPDATETEXT 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 2
No 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:

Mensaje 11427, nivel 16, estado 1
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.