2011-08-07 5 views
0

게시 된 메타 데이터 레이어의 변경 사항에 따라 SQL2K5 테이블 구조를 동적으로 변경하는 프로세스가 있습니다.INFORMATION_SCHEMA를 쿼리 할 때 교착 상태가 발생했습니다.

예를 들어, 새 열을 추가해야하고 테이블에 종속성이없는 경우 - 단계는 입니다. 1. 테이블에 이미 존재하는 모든 인덱스 & 기본 키에 대해 T-SQL을 사용하여 스크립트를 만듭니다. 스크립트는 이하에 2. 드롭 테이블 3. 새로운 열이 메타 층에서 테이블을 다시 작성을 포함 4. 대량 복사

사용하여 테이블을 채우는 단계 # 1 5에서 생성 된 스크립트를 실행

위는 .NET 어셈블리를 통해 시작되며 매일 3 개의 동시 스트림에서 실행됩니다.

1 단계에서 인덱스/키를 스크립팅하기 위해 INFORMATION_SCHEMA 테이블에 액세스 할 때 교착 상태 오류가 발생합니다. 이 스크립트에서 힌트 WITH (NOLOCK)을 사용하여 이러한 작업의 스트림 3 개가 동시에 실행될 때 잠금을 방지해야한다고 생각했습니다. 테이블은 하나의 스트림에서만 처리 (작성 또는 스크립팅) 될 수 있습니다.

내가 할 일이 더 많습니까 ???

모든 의견을 매우 높이 평가합니다. 뷰 -

[스크립트]

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에 대한 호출에 폭탄이 있는지 여부는 로그에서 알 수 없습니다. 내 CREATE TABLE 스크립트를 생성 할 때 – simon

+0

나는 다음과 같이 시작합니다 는 'SQL은 & vbNewLine에게 TSQL을 TSQL = "[& info.SQLServerDestDatabase &"] "사용"& vbNewLine TSQL & = "ANSI_NULLS ON 설정"및 구축 = vbNewLine & TSQL을 "ON QUOTED_IDENTIFIER 설정"& = "TABLE [dbo가] CREATE."& info.SQLServerDestTableName & "("& vbNewLine \t \t ... 나는 단순히 사용하여 문을 실행 : SqlHelper를 .ExecuteNonQuery (sqlConnString, CommandType.Text, tsql) 이 시점까지 교착 상태로부터 보호 할 수있는 방법이 있습니까? – simon

+0

다시 말하지만, 귀하의 의견에 "교착 상태가 발생했습니다. 데드락! "스크립트의 어느 부분에서 교착 상태가 발생 하는지를 추측하는 것보다 정확하게 정확하게 고정해야합니다. 두 경우 모두 스토어드 프로 시저는 커서를 빌드 한 후 SELECT를 실행하기 만하므로 커서가 있어야합니다. 다른 곳에서 (나는 당신이 절차를 호출 한 후 용의자가된다.) 할 일은 커서를 고치는 것일 것이다 ... –

관련 문제