Sqlserver
 sql >> Baza danych >  >> RDS >> Sqlserver

Zakleszczenie podczas wysyłania zapytań do INFORMATION_SCHEMA

  1. Widoki INFORMATION_SCHEMA są właśnie tym - widokami. Nie można ich aktualizować, więc jest mało prawdopodobne, aby powodowały zakleszczenie. Jeśli chcesz określić prawdziwe źródło (które, jak zakładam, ma coś wspólnego z twoimi alter, lub innym kodem w obrębie kursora, którego nie pokazałeś, lub innym kodem, który wywołujesz w połączeniu z wywoływaniem tych procedur - ponieważ wybiera przeciwko widoki, a potem wybieranie zmiennych nie może być przyczyną), proponuję przeczytać Wpis na blogu Gail Shaw na temat interpretacji impasów .

  2. Pomimo (1) nadal sugeruję używanie bardziej nowoczesnych widoków katalogu niż INFORMATION_SCHEMA. Te same informacje można uzyskać na przykład z sys.key_constraints.

  3. Używasz domyślnych opcji kursora; i zagnieżdżasz kursory. Jeśli nadal będziesz używać kursorów, powinieneś przyzwyczaić się do używania mniej zasobożernego kursora (np. LOCAL STATIC FORWARD_ONLY READ_ONLY).

  4. W rzeczywistości nie potrzebujesz do tego kursora. Oto jak przepisałbym skrypt tabeli 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
    

Jeśli chodzi o skrypt tworzenia indeksu, myślę, że jest lepszy sposób na zrobienie tego (znowu bez wyraźnych kursorów, nie żeby unikanie kursora było celem, ale kod będzie DUŻO czystszy). Najpierw potrzebujesz funkcji do zbudowania klucza lub włączenia kolumn z indeksu:

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

Po uruchomieniu tej funkcji procedura ScriptIndexes jest całkiem prosta:

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

Zwróć uwagę, że moje rozwiązanie nie zakłada, że ​​PK jest klastrowany (Twój skrypt PK koduje na stałe CLUSTERED, ale skrypt indeksu zakłada, że ​​którykolwiek z indeksów może być klastrowany). Ignoruję również dodatkowe właściwości, takie jak grupa plików, partycjonowanie lub filtrowane indeksy (i tak nie są obsługiwane w 2005).




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. SQL Server - użycie CASE w klauzuli WHERE

  2. jak rozdzielić ciąg na różne kolumny?

  3. SQL, aby znaleźć pierwszy nienumeryczny znak w łańcuchu

  4. Jak mogę pogrupować według kolumny daty i godziny bez uwzględniania czasu?

  5. SQL server 2008 R2, wybierz jedną wartość kolumny dla każdej odrębnej wartości innej kolumny