2011-08-07 18 views
0

私は公開されたメタデータ・レイヤーの変更に従って自分のSQL2K5テーブル構造を動的に変更するプロセスを持っています。例えばINFORMATION_SCHEMAを照会するときにデッドロックが発生する

、新しい列を追加する必要があり、表がNO依存関係を持っていない場合 - 手順は次のようになります。 1.すべてのインデックスのためのT-SQLを使用してスクリプトを作成します&すでにテーブルに存在する主キーを[これらのスクリプトは]以下 2.ドロップテーブル 3.新しい列を持っているメタ層からテーブルを再作成しますが含まれている 4.バルク・コピー・

使用してテーブルを読み込むステップ#1 5で作成したスクリプトを実行します

上記は.NETアセンブリを介して開始され、3つの同時ストリームで毎日実行されます。

インデックス/キーをスクリプト化するINFORMATION_SCHEMAテーブルにアクセスすると、ステップ1でデッドロックエラーが発生します。私はこれらのスクリプトのヒント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,' ') 
+0

デッドロックの原因となっているプロシージャ、主キーをスクリプト化するプロシージャ、またはインデックスをスクリプト化するプロシージャはどれですか?なぜsys.key_constraints、sys.index_columns、sys.indexes.is_clusteredなどの代わりにINFORMATION_SCHEMA、INDEXPROPERTYなどを使用していますか? –

答えて

1
  1. INFORMATION_SCHEMAビューはまさにそれです。デッドロックが発生しないように更新することはできません。実際のソース(あなたが変更したもの、または表示していないカーソル内の他のコード、またはこれらのプロシージャを呼び出すことと組み合わせて呼び出す他のコードとは何かがあると想定します)は、変数を選択することは原因ではありません)、私はGail Shaw's blog post on interpreting deadlocksを読むことをお勧めします。

  2. (1)でも、私はINFORMATION_SCHEMAよりも最新のカタログビューを使用することをお勧めします。同じ情報は、たとえばsys.key_constraintsから取得できます。

  3. デフォルトのカーソルオプションを使用しています。あなたはカーソルを入れ子にしています。カーソルを使用して終了する場合は、リソースの消費量の少ないカーソル(LOCAL STATIC FORWARD_ONLY READ_ONLYなど)を使用する傾向があります。

  4. これを行うには、実際にはカーソルは必要ありません。

    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 
    
    は、インデックス作成スクリプト用として

、私は避けていないことを、明示カーソルなしで、再びこれを(行うには良い方法があると思います。ここで私はPKテーブルスクリプトを再書き込みする方法をありますカーソルが目標ですが、コードは多くのクリーナーになります)。まず、あなたがインデックスから列をどちらかがキー構築する機能を必要とするか、次のとおりの場所でその機能付

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ではサポートされていません)。

+0

あなたのコメントとスクリプトAaronに感謝します! (私は間違いなくあなたのインデックス作成スクリプトを見ることに興味があります) エラーは、テーブルを作成/変更するのではなくスクリプティング手順から発生します。影響。 しかし、インデックスへの呼び出しやプライマリキーの格納されたprocに爆弾かどうかは、私のログからは分かりません。 – simon

+0

私は、CREATE TABLEスクリプトを作成するとき、私は次のように起動します。 は「SQL TSQL = "[info.SQLServerDestDatabase&& "]" &vbNewLine USE" TSQL&= "SET ANSI_NULLS ON" &vbNewLine TSQLを構築& = "SET QUOTED_IDENTIFIER ON" &vbNewLine TSQL&= "[&info.SQLServerDestTableName& "](" &vbNewLine \t \t ... 私は、単に使用して文を実行し、[DBO]テーブルを作成します。": SqlHelperを.ExecuteNonQuery(sqlConnString、CommandType.Text、tsql) この時点までに至るデッドロックから保護できることはありますか? – simon

+0

もう一度、私はあなたのコメントで "デッドロックを叫ぶだろう何かを見ていないよ!デッドロック!デッドロックにつながる部分を推測するよりも正確にピンを固定する必要があります。どちらの場合でも、ストアドプロシージャはカーソルを作成した後にSELECTを実行するだけなので、他の場所では(私はあなたがプロシージャを呼び出した後に疑います)。まず、カーソルを修正することです... –

関連する問題