Тупик при запросе INFORMATION_SCHEMA - PullRequest
0 голосов
/ 08 августа 2011

У меня есть процесс, который динамически изменяет структуру моей таблицы SQL2K5 в соответствии с изменениями в опубликованном слое метаданных.

Например, если необходимо добавить новый столбец, и у таблицы НЕТ зависимостей -следующие шаги: 1. Создать сценарии с использованием T-SQL для любых индексов и первичных ключей, которые уже существуют в таблице [эти сценарии включены ниже] 2. Удалить таблицу 3. Повторно создать таблицу из мета-слоя, который имеетновый столбец 4. Выполните сценарии, созданные на шаге № 1. 5. Заполните таблицу, используя BulkCopy

. Вышеуказанное инициируется с помощью сборки .NET и выполняется в 3 одновременных потока ежедневно.

Я получаю ошибку взаимоблокировки на шаге 1 - когда я получаю доступ к таблицам INFORMATION_SCHEMA для составления сценария индексов / ключей.Я использовал подсказку WITH (NOLOCK) в этих сценариях, думая, что это должно предотвратить любую блокировку, когда 3 потока этих действий выполняются одновременно.Таблицу можно обрабатывать (создавать или создавать сценарии) только в 1 потоке.

Есть ли что-то еще, что мне нужно сделать ???

Любые комментарии с благодарностью.

[Скрипты]

ALTER Procedure [dbo].[s$spScriptPrimaryKeyForTable]
@Tablename varchar(100)
AS 


-- Get all existing primary keys
DECLARE cPK CURSOR FOR
SELECT TABLE_NAME, CONSTRAINT_NAME 
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WITH(NOLOCK)
WHERE upper(TABLE_NAME)=upper(@Tablename)
ORDER BY TABLE_NAME

DECLARE @PkTable SYSNAME
DECLARE @PkName SYSNAME

-- Loop through all the primary keys
OPEN cPK
FETCH NEXT FROM cPK INTO @PkTable, @PkName
WHILE (@@FETCH_STATUS = 0)
BEGIN
DECLARE @PKSQL NVARCHAR(4000) SET @PKSQL = ''
SET @PKSQL = 'ALTER TABLE ' + @PkTable + ' ADD CONSTRAINT ' + @PkName + ' PRIMARY KEY CLUSTERED ('

-- Get all columns for the current primary key
DECLARE cPKColumn CURSOR FOR
SELECT COLUMN_NAME 
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WITH(NOLOCK)
WHERE TABLE_NAME = @PkTable AND CONSTRAINT_NAME = @PkName
ORDER BY ORDINAL_POSITION
OPEN cPKColumn

DECLARE @PkColumn SYSNAME
DECLARE @PkFirstColumn BIT SET @PkFirstColumn = 1
-- Loop through all columns and append the sql statement
FETCH NEXT FROM cPKColumn INTO @PkColumn
WHILE (@@FETCH_STATUS = 0)
BEGIN
IF (@PkFirstColumn = 1)
SET @PkFirstColumn = 0
ELSE
SET @PKSQL = @PKSQL + ', '

SET @PKSQL = @PKSQL + @PkColumn

FETCH NEXT FROM cPKColumn INTO @PkColumn
END
CLOSE cPKColumn
DEALLOCATE cPKColumn

SET @PKSQL = @PKSQL + ')'
-- Print the primary key statement
-- PRINT @PKSQL

FETCH NEXT FROM cPK INTO @PkTable, @PkName
END
CLOSE cPK
DEALLOCATE cPK


SELECT ISNULL(@PKSQL,' ')

================

ALTER Procedure [dbo].[s$spScriptIndexesForTable]
@Tablename varchar(100)

AS 

DECLARE @RetVal varchar(4000)
SET @RetVal = ''

-- Get all existing indexes, but NOT the primary keys 
DECLARE cIX CURSOR FOR 
SELECT OBJECT_NAME(SI.Object_ID), SI.Object_ID, SI.Name, SI.Index_ID 
FROM Sys.Indexes SI WITH(NOLOCK)
LEFT JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS TC WITH(NOLOCK) ON SI.Name = TC.CONSTRAINT_NAME AND OBJECT_NAME(SI.Object_ID) = TC.TABLE_NAME 
WHERE TC.CONSTRAINT_NAME IS NULL 
AND OBJECTPROPERTY(SI.Object_ID, 'IsUserTable') = 1 
AND upper(OBJECT_NAME(SI.Object_ID))=upper(@Tablename)
ORDER BY OBJECT_NAME(SI.Object_ID), SI.Index_ID 

DECLARE @IxTable SYSNAME 
DECLARE @IxTableID INT 
DECLARE @IxName SYSNAME 
DECLARE @IxID INT 

-- Loop through all indexes 
OPEN cIX 
FETCH NEXT FROM cIX INTO @IxTable, @IxTableID, @IxName, @IxID 
WHILE (@@FETCH_STATUS = 0) 
BEGIN 
DECLARE @IXSQL NVARCHAR(4000) 
--SET @PKSQL = '' 
SET @IXSQL = 'CREATE ' 

-- Check if the index is unique 
IF (INDEXPROPERTY(@IxTableID, @IxName, 'IsUnique') = 1) 
SET @IXSQL = @IXSQL + 'UNIQUE ' 
-- Check if the index is clustered 
IF (INDEXPROPERTY(@IxTableID, @IxName, 'IsClustered') = 1) 
SET @IXSQL = @IXSQL + 'CLUSTERED ' 

SET @IXSQL = @IXSQL + 'INDEX ' + @IxName + ' ON [' + @IxTable + '] (' 

-- Get all columns of the index 
DECLARE cIxColumn CURSOR FOR 
SELECT SC.Name,IC.[is_included_column],IC.is_descending_key 
FROM Sys.Index_Columns IC WITH(NOLOCK)
JOIN Sys.Columns SC WITH(NOLOCK) ON IC.Object_ID = SC.Object_ID AND IC.Column_ID = SC.Column_ID 
WHERE IC.Object_ID = @IxTableID AND Index_ID = @IxID 
ORDER BY IC.Index_Column_ID,IC.is_included_column 

DECLARE @IxColumn SYSNAME 
DECLARE @IxIncl bit 
DECLARE @Desc bit 
DECLARE @IxIsIncl bit set @IxIsIncl = 0 
DECLARE @IxFirstColumn BIT SET @IxFirstColumn = 1 

-- Loop throug all columns of the index and append them to the CREATE statement 
OPEN cIxColumn 
FETCH NEXT FROM cIxColumn INTO @IxColumn, @IxIncl, @Desc 
WHILE (@@FETCH_STATUS = 0) 
BEGIN 

IF (@IxFirstColumn = 1) 
BEGIN 
SET @IxFirstColumn = 0 
END 
ELSE 
BEGIN 
--check to see if it's an included column 
IF ((@IxIsIncl = 0) AND (@IxIncl = 1)) 
BEGIN 
SET @IxIsIncl = 1 
SET @IXSQL = @IXSQL + ') INCLUDE (' 
END 
ELSE 
BEGIN 
SET @IXSQL = @IXSQL + ', ' 
END 
END 

SET @IXSQL = @IXSQL + '[' + @IxColumn + ']' 
--check to see if it's DESC 
IF @Desc = 1 
SET @IXSQL = @IXSQL + ' DESC' 

FETCH NEXT FROM cIxColumn INTO @IxColumn, @IxIncl, @Desc 
END 
CLOSE cIxColumn 
DEALLOCATE cIxColumn 

SET @IXSQL = @IXSQL + ')' 

-- Print out the CREATE statement for the index 
--SELECT 'IXSQL: ' + @IXSQL
IF @RetVal IS NULL
SET @RetVal = ''
--SELECT 'Retval: ' + @RetVal
SET @RetVal = @RetVal + @IXSQL + ' ' 

FETCH NEXT FROM cIX INTO @IxTable, @IxTableID, @IxName, @IxID 
END 

CLOSE cIX 
DEALLOCATE cIX 

SELECT ISNULL(@RetVal,' ')

1 Ответ

1 голос
/ 08 августа 2011
  1. Представления INFORMATION_SCHEMA - это просто представления.Вы не можете обновить их, поэтому они вряд ли вызовут какие-либо тупики.Если вы хотите определить реальный источник (который, как я предполагаю, как-то связан с вашими изменениями, или другим кодом в пределах курсора, который вы не показывали, или другим кодом, который вы вызываете в сочетании с вызовом этих процедур), так как выбирает противпросмотры, а затем выбор переменных не могут быть причиной), я предлагаю прочитать сообщение в блоге Гейл Шоу о интерпретации тупиков .

  2. Несмотря на (1) я все ещепредложить использовать более современные виды каталога, чем INFORMATION_SCHEMA.Та же информация может быть получена, например, из sys.key_constraints.

  3. Вы используете параметры курсора по умолчанию;и вы вкладываете курсоры.Если вы в конечном итоге все еще используете курсоры, вы должны привыкнуть использовать менее ресурсоемкий курсор (например, LOCAL STATIC FORWARD_ONLY READ_ONLY).

  4. На самом деле вам не нужен курсор для этого.Вот как я бы переписал скрипт таблицы 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
    

Что касается скрипта создания индекса, я думаю, что есть лучший способ сделать это (опять же без явных курсоровне то, чтобы избежать курсора - это цель, но код будет намного чище).Сначала вам нужна функция для построения ключа или включения столбцов из индекса:

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

С этой функцией процедура ScriptIndexes довольно проста:

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

Обратите внимание, что мойРешение не предполагает кластеризацию PK (жесткие коды вашего PK-скрипта CLUSTERED, но тогда ваш индексный скрипт предполагает, что любой из индексов может быть кластеризованным).Я также игнорирую дополнительные свойства, такие как файловая группа, разделы или отфильтрованные индексы (в любом случае не поддерживаются в 2005 году).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...