Sqlserver
 sql >> Base de données >  >> RDS >> Sqlserver

Interblocage lors de la requête INFORMATION_SCHEMA

  1. Les vues INFORMATION_SCHEMA ne sont que cela - des vues. Vous ne pouvez pas les mettre à jour, il est donc peu probable qu'ils provoquent des blocages. Si vous voulez déterminer la source réelle (ce qui, je suppose, a quelque chose à voir avec vos modifications, ou un autre code dans le curseur que vous n'avez pas montré, ou un autre code que vous appelez en combinaison avec l'appel de ces procédures - car sélectionne contre vues puis la sélection de variables ne peuvent pas en être la cause), je suggère de lire Article de blog de Gail Shaw sur l'interprétation des blocages .

  2. Malgré (1), je suggère toujours d'utiliser des vues de catalogue plus modernes que INFORMATION_SCHEMA. Les mêmes informations peuvent être dérivées, par exemple, de sys.key_constraints.

  3. Vous utilisez les options de curseur par défaut ; et vous imbriquez des curseurs. Si vous continuez à utiliser des curseurs, vous devriez prendre l'habitude d'utiliser un curseur moins gourmand en ressources (par exemple, LOCAL STATIC FORWARD_ONLY READ_ONLY).

  4. Vous n'avez pas réellement besoin d'un curseur pour le faire. Voici comment je réécrirais le script de table 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 ce qui concerne le script de création d'index, je pense qu'il existe une meilleure façon de le faire (encore une fois sans curseurs explicites, non pas qu'éviter le curseur soit le but, mais le code va être un nettoyeur LOT). Vous avez d'abord besoin d'une fonction pour créer une clé ou inclure des colonnes à partir de l'index :

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

Avec cette fonction en place, une procédure ScriptIndexes est assez simple :

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

Notez que ma solution ne suppose pas que le PK est en cluster (votre script PK code en dur CLUSTERED mais ensuite votre script d'index suppose que l'un des index peut être en cluster). J'ignore également les propriétés supplémentaires telles que le groupe de fichiers, le partitionnement ou les index filtrés (non pris en charge en 2005 de toute façon).