sql >> Database >  >> RDS >> Sqlserver

Deadlock bij het opvragen van INFORMATION_SCHEMA

  1. INFORMATION_SCHEMA weergaven zijn precies dat - weergaven. U kunt ze niet bijwerken, dus het is onwaarschijnlijk dat ze een impasse veroorzaken. Als je de echte bron wilt bepalen (waarvan ik aanneem dat het iets te maken heeft met je alters, of andere code binnen de cursor die je niet liet zien, of andere code die je aanroept in combinatie met het aanroepen van deze procedures - aangezien selecteert tegen weergaven en het selecteren van variabelen kan niet de oorzaak zijn), raad ik aan Gail Shaw's blogbericht over het interpreteren van impasses .

  2. Ondanks (1) raad ik nog steeds aan om modernere catalogusweergaven te gebruiken dan INFORMATION_SCHEMA. Dezelfde informatie kan bijvoorbeeld worden afgeleid uit sys.key_constraints.

  3. U gebruikt de standaard cursoropties; en je nestt cursors. Als je uiteindelijk nog steeds cursors gebruikt, moet je er een gewoonte van maken om een ​​minder resource-intensieve cursor te gebruiken (bijv. LOCAL STATIC FORWARD_ONLY READ_ONLY).

  4. Je hebt hiervoor eigenlijk geen cursor nodig. Hier is hoe ik het PK-tabelscript zou herschrijven:

    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
    

Wat betreft het script voor het maken van een index, ik denk dat er een betere manier is om dit te doen (opnieuw zonder expliciete cursors, niet dat het vermijden van de cursor het doel is, maar de code zal VEEL schoner zijn). Eerst heb je een functie nodig om een ​​sleutel te bouwen of kolommen uit de index op te nemen:

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

Met die functie is een ScriptIndexes-procedure vrij eenvoudig:

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

Merk op dat mijn oplossing er niet van uitgaat dat de PK geclusterd is (uw PK-script hardcodes CLUSTERED maar dan gaat uw indexscript ervan uit dat een van de indexen geclusterd zou kunnen zijn). Ik negeer ook extra eigenschappen zoals bestandsgroep, partitionering of gefilterde indexen (in ieder geval niet ondersteund in 2005).




  1. SQL:Een relatietabel maken met 2 verschillende auto_increment

  2. Oracle:Wat betekent 'direct uitvoeren'?

  3. Waarom retourneert dit Resource-id #2?

  4. Opgeslagen procedure-uitvoerparameters in SQL Server Profiler