sql >> Databasteknik >  >> RDS >> Sqlserver

Dödläge när man frågar INFORMATION_SCHEMA

  1. INFORMATION_SCHEMA vyer är just det - visningar. Du kan inte uppdatera dem så det är osannolikt att de orsakar låsningar. Om du vill bestämma den verkliga källan (som jag antar har något att göra med dina ändringar, eller annan kod i markören som du inte visade, eller annan kod du anropar i kombination med att anropa dessa procedurer - eftersom väljer mot visningar och sedan välja variabler kan inte vara orsaken), föreslår jag att du läser Gail Shaws blogginlägg om tolkning av dödlägen .

  2. Trots (1) föreslår jag fortfarande att du använder mer moderna katalogvyer än INFORMATION_SCHEMA. Samma information kan härledas från till exempel sys.key_constraints.

  3. Du använder standardmarköralternativen; och du kapar markörer. Om du fortfarande använder markörer bör du ta för vana att använda en mindre resurskrävande markör (t.ex. LOCAL STATIC FORWARD_ONLY READ_ONLY).

  4. Du behöver faktiskt ingen markör för att göra detta. Så här skulle jag skriva om PK-tabellskriptet:

    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
    

När det gäller skriptet för att skapa index, tror jag att det finns ett bättre sätt att göra detta (igen utan explicita markörer, inte för att undvika markören är målet, men koden kommer att bli mycket renare). Först behöver du en funktion för att bygga antingen nyckel eller inkludera kolumner från indexet:

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

Med den funktionen på plats är en ScriptIndex-procedur ganska enkel:

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

Observera att min lösning inte förutsätter att PK är klustrat (ditt PK-skript hårdkoder CLUSTERED men sedan antar ditt indexskript att något av indexen kan klustras). Jag ignorerar också ytterligare egenskaper som filgrupp, partitionering eller filtrerade index (stöds inte 2005 i alla fall).




  1. 2 sätt att få det korta månadsnamnet från ett datum i MariaDB

  2. USER-funktion i Oracle

  3. Förloppsindikator för lagrad procedur

  4. Hur man hämtar exakt matchande poster i Spring JPA @Query