sql >> Base de Datos >  >> RDS >> Sqlserver

Interbloqueo al consultar INFORMACION_ESQUEMA

  1. Las vistas de INFORMATION_SCHEMA son solo eso:vistas. No puede actualizarlos, por lo que es poco probable que provoquen interbloqueos. Si desea determinar la fuente real (lo que supongo que tiene algo que ver con sus alteraciones u otro código dentro del cursor que no mostró, u otro código que está llamando en combinación con llamar a estos procedimientos, ya que selecciona contra vistas y luego seleccionar variables no puede ser la causa), sugiero leer Publicación de blog de Gail Shaw sobre la interpretación de interbloqueos .

  2. A pesar de (1) todavía sugiero usar vistas de catálogo más modernas que INFORMACIÓN_ESQUEMA. La misma información se puede derivar de, por ejemplo, sys.key_constraints.

  3. Está utilizando las opciones de cursor predeterminadas; y estás anidando cursores. Si termina usando cursores, debe acostumbrarse a usar un cursor que consuma menos recursos (por ejemplo, LOCAL STATIC FORWARD_ONLY READ_ONLY).

  4. En realidad no necesitas un cursor para hacer esto. Así es como volvería a escribir el script de la tabla PK:

    CREATE PROCEDURE dbo.ScriptPKForTable
        @TableName SYSNAME
    AS
    BEGIN
        SET NOCOUNT ON;
    
        DECLARE 
          @pkName    SYSNAME,
          @clustered BIT,
          @object_id INT,
          @sql       NVARCHAR(MAX);
    
        SELECT
          @object_id = OBJECT_ID(UPPER(@TableName));
    
        SELECT
          @pkName = kc.name,
          @clustered = CASE i.[type] 
            WHEN 1 THEN 1 ELSE 0 END
        FROM 
            sys.key_constraints AS kc
        INNER JOIN 
            sys.indexes AS i
            ON kc.parent_object_id = i.[object_id]
            AND kc.unique_index_id = i.index_id
        WHERE
            kc.parent_object_id = @object_id
            AND kc.[type] = 'pk';
    
        SET @sql = N'ALTER TABLE ' + QUOTENAME(@TableName)
          + ' ADD CONSTRAINT ' + @pkName 
          + ' PRIMARY KEY ' + CASE @clustered 
          WHEN 1 THEN 'CLUSTERED' ELSE '' END + ' (';
    
        SELECT
          @sql = @sql + c.name + ','
        FROM 
          sys.index_columns AS ic
        INNER JOIN
          sys.indexes AS i 
          ON ic.index_id = i.index_id
          AND ic.[object_id] = i.[object_id]
        INNER JOIN 
          sys.key_constraints AS kc
          ON i.[object_id] = kc.[parent_object_id]
          AND kc.unique_index_id = i.index_id
        INNER JOIN 
          sys.columns AS c
          ON i.[object_id] = c.[object_id]
          AND ic.column_id = c.column_id
        WHERE
          kc.[type] = 'PK'
          AND kc.parent_object_id = @object_id
        ORDER BY key_ordinal;
    
        SET @sql = LEFT(@sql, LEN(@sql) - 1) + ');';
    
        SELECT COALESCE(@sql, ' ');
    END
    GO
    

En cuanto a la secuencia de comandos de creación de índices, creo que hay una mejor manera de hacerlo (nuevamente sin cursores explícitos, no es que evitar el cursor sea el objetivo, pero el código será MUCHO más limpio). Primero necesita una función para construir una clave o incluir columnas del índice:

CREATE FUNCTION dbo.BuildIndexColumns
(
    @object_id        INT,
    @index_id         INT,
    @included_columns BIT
)
RETURNS NVARCHAR(MAX)
AS
BEGIN
  DECLARE @s NVARCHAR(MAX);

  SELECT @s = N'';

  SELECT @s = @s + c.name + CASE ic.is_descending_key
    WHEN 1 THEN ' DESC' ELSE '' END + ',' 
    FROM sys.index_columns AS ic
    INNER JOIN sys.columns AS c
    ON ic.[object_id] = c.[object_id]
    AND ic.column_id = c.column_id
    WHERE c.[object_id] = @object_id
    AND ic.[object_id] = @object_id
    AND ic.index_id = @index_id
    AND ic.is_included_column = @included_columns
    ORDER BY ic.key_ordinal;

  IF @s > N''
    SET @s = LEFT(@s, LEN(@s)-1);

  RETURN (NULLIF(@s, N''));
END
GO

Con esa función en su lugar, un procedimiento de ScriptIndexes es bastante fácil:

CREATE PROCEDURE dbo.ScriptIndexesForTable
    @TableName SYSNAME
AS
BEGIN
  SET NOCOUNT ON;

  DECLARE
      @sql       NVARCHAR(MAX),
      @object_id INT;

  SELECT @sql = N'', @object_id = OBJECT_ID(UPPER(@TableName));

  SELECT @sql = @sql + 'CREATE '
      + CASE i.is_unique WHEN 1 THEN 'UNIQUE ' ELSE '' END
      + CASE i.[type] WHEN 1 THEN 'CLUSTERED ' ELSE '' END
      + ' INDEX ' + i.name + ' ON ' + QUOTENAME(@TableName) + ' (' 
      + dbo.BuildIndexColumns(@object_id, i.index_id, 0)
      + ')' + COALESCE(' INCLUDE(' 
      + dbo.BuildIndexColumns(@object_id, i.index_id, 1)
      + ')', '') + ';' + CHAR(13) + CHAR(10)
  FROM
      sys.indexes AS i
  WHERE
      i.[object_id] = @object_id
      -- since this will be covered by ScriptPKForTable:
      AND i.is_primary_key = 0
  ORDER BY i.index_id;

  SELECT COALESCE(@sql, ' ');
END
GO

Tenga en cuenta que mi solución no asume que el PK está agrupado (su secuencia de comandos PK codifica CLUSTERED pero luego su secuencia de comandos de índice asume que cualquiera de los índices podría estar agrupado). También ignoro propiedades adicionales, como grupos de archivos, particiones o índices filtrados (de todos modos, no se admiten en 2005).