EDITこの解決策は、10 年以上にわたる開発 (「適切なデータベース設計」を試みて、最終的には崩壊します。)
END EDIT
PK をドロップして再作成する必要があると言った人にとって、あなたは正しかったです。インデックスと外部キーも削除して再作成する必要がありました。
幸いなことに、管理可能な数のインデックスと FK があったので、これらを「例外的」として処理し、スクリプトの先頭で一度に 1 つずつすべてを削除し、最後に一度に 1 つずつ再度追加しました。スクリプトの (以下の /* */ の 2 つのセクションを参照してください)。
次に、SQL スクリプトの本体は、FK に関する完全な詳細を一時テーブルに入力し、各テーブル名をループして、FK を削除し、データ型を変更して、FK を再度追加します。
アセンブルされる SQL 文字列は、以下のスクリプトで PRINT されます。これを再利用する場合 (保証が提供されないなど)、これらをコメントアウトすると、実行時間が最大 50% 短縮されます。
SET NOCOUNT ON
/* Handle exceptional tables here
* Remove indexes and foreign keys
* --Lots of "IF EXISTS ... ALTER TABLE <name> DROP CONSTRAINT <constraint name>, etc.
*/
--Declare variables
DECLARE @SQL VARCHAR(8000)
DECLARE @TableName VARCHAR(512)
DECLARE @ConstraintName VARCHAR(512)
DECLARE @tColumn VARCHAR(512)
DECLARE @Columns VARCHAR(8000)
DECLARE @IsDescending BIT
--Set up temporary table
SELECT
tbl.[schema_id],
tbl.name AS TableName,
i.NAME AS IndexName,
i.type_desc,
c.[column],
c.key_ordinal,
c.is_desc,
i.[object_id],
s.no_recompute,
i.[ignore_dup_key],
i.[allow_row_locks],
i.[allow_page_locks],
i.[fill_factor],
dsi.type,
dsi.name AS DataSpaceName
INTO #PKBackup
FROM
sys.tables AS tbl
INNER JOIN sys.indexes AS i
ON (
i.index_id > 0
AND i.is_hypothetical = 0
)
AND ( i.[object_id] = tbl.[object_id] )
INNER JOIN (
SELECT
ic.[object_id] ,
c.[name] [column] ,
ic.is_descending_key [is_desc],
ic.key_ordinal
FROM
sys.index_columns ic
INNER JOIN
sys.indexes i
ON
i.[object_id] = ic.[object_id]
AND
i.index_id = 1
AND
ic.index_id = 1
INNER JOIN
sys.tables t
ON
t.[object_id] = ic.[object_id]
INNER JOIN
sys.columns c
ON
c.[object_id] = t.[object_id]
AND
c.column_id = ic.column_id
) AS c
ON c.[object_id] = i.[object_id]
LEFT OUTER JOIN
sys.key_constraints AS k
ON
k.parent_object_id = i.[object_id]
AND
k.unique_index_id = i.index_id
LEFT OUTER JOIN
sys.data_spaces AS dsi
ON
dsi.data_space_id = i.data_space_id
LEFT OUTER JOIN
sys.xml_indexes AS xi
ON
xi.[object_id] = i.[object_id]
AND
xi.index_id = i.index_id
LEFT OUTER JOIN
sys.stats AS s
ON
s.stats_id = i.index_id
AND
s.[object_id] = i.[object_id]
WHERE
k.TYPE = 'PK'
DECLARE TableCursor CURSOR LOCAL READ_ONLY FOR
SELECT t.name AS TableName
FROM sys.columns c
JOIN sys.tables t ON c.object_id = t.object_id
WHERE
c.name = 'ReferenceID'
OPEN TableCursor
FETCH NEXT FROM TableCursor
INTO @TableName
WHILE @@FETCH_STATUS = 0
BEGIN
PRINT('--Updating ' + @TableName + '...')
SELECT @ConstraintName = PK.CONSTRAINT_NAME
FROM
INFORMATION_SCHEMA.TABLE_CONSTRAINTS PK
WHERE
PK.TABLE_NAME = @TableName
AND
PK.CONSTRAINT_TYPE = 'PRIMARY KEY'
--drop the constraint
--Some tables don't have a PK defined, only do the next bit if they do
IF (SELECT COUNT(*) FROM #PKBackup PK WHERE PK.TableName = @TableName) > 0
BEGIN
SET @SQL = 'ALTER TABLE @TableName DROP CONSTRAINT @ConstraintName'
SET @SQL = REPLACE(@SQL, '@TableName', @TableName)
SET @SQL = REPLACE(@SQL, '@ConstraintName', @ConstraintName)
PRINT @SQL
EXEC (@SQL)
END
--This is where we actually change the datatype of the column
SET @SQL = 'ALTER TABLE @TableName ALTER COLUMN ReferenceID VARCHAR(8)' + (SELECT CASE WHEN C.Is_Nullable = 'NO' THEN ' NOT NULL' ELSE '' END
FROM INFORMATION_SCHEMA.COLUMNS C
WHERE C.TABLE_NAME = @TableName AND C.COLUMN_NAME = 'ReferenceID')
SET @SQL = REPLACE(@SQL, '@TableName', @TableName)
PRINT(@SQL)
EXEC(@SQL)
--Recreate the constraint
--Some tables don't have a PK defined, only do the next bit if they do
IF (SELECT COUNT(*) FROM #PKBackup PK WHERE PK.TableName = @TableName) > 0
BEGIN
--First set up @SQL template
SELECT @SQL = 'ALTER TABLE [' + SCHEMA_NAME(PK.schema_id) + '].[' + PK.TableName
+ '] ADD CONSTRAINT [' + PK.IndexName
+ '] PRIMARY KEY ' + Type_desc + ' ( @Columns ) WITH '
+ '( PAD_INDEX = ' + CASE WHEN CAST(INDEXPROPERTY(pk.[object_id], PK.IndexName, N'IsPadIndex') AS BIT) = 0 THEN 'OFF'
ELSE 'ON'
END + ', '
+ 'STATISTICS_NORECOMPUTE = ' + CASE WHEN pk.no_recompute = 0 THEN 'OFF'
ELSE 'ON'
END
+ ', SORT_IN_TEMPDB = OFF, '
+ 'IGNORE_DUP_KEY = ' + CASE WHEN pk.[ignore_dup_key] = 0 THEN 'OFF'
ELSE 'ON'
END + ', '
+ 'ONLINE = OFF, '
+ 'ALLOW_ROW_LOCKS = ' + CASE WHEN pk.allow_row_locks = 0 THEN 'OFF'
ELSE 'ON'
END + ', '
+ 'ALLOW_PAGE_LOCKS = ' + CASE WHEN pk.allow_page_locks = 0 THEN 'OFF'
ELSE 'ON'
END + ', '
+ 'FILLFACTOR = ' + CASE WHEN pk.[fill_factor] = 0 THEN '100'
ELSE CONVERT(NVARCHAR, pk.[fill_factor])
END + ' '
+ ') ON [' + CASE WHEN 'FG' = pk.[type] THEN pk.DataSpaceName
ELSE N''
END + ']'
FROM
#PKBackup PK WHERE PK.TableName = @TableName
SET @SQL = REPLACE(@SQL, '@TableName', @TableName)
SET @SQL = REPLACE(@SQL, '@ConstraintName', @ConstraintName)
--Second, build up @Columns
SET @Columns = ' '
DECLARE ColumnCursor CURSOR LOCAL READ_ONLY FOR
SELECT pk.[column], PK.is_desc
FROM #PKBackup PK
WHERE PK.TableName = @TableName
ORDER BY PK.key_ordinal ASC
OPEN ColumnCursor
FETCH NEXT FROM ColumnCursor
INTO @tColumn, @IsDescending
WHILE @@FETCH_STATUS = 0
BEGIN
SET @Columns = @Columns + @tColumn + CASE WHEN @IsDescending = 1 THEN ' DESC, ' ELSE ' ASC, ' END
--Get the next TableName
FETCH NEXT FROM ColumnCursor
INTO @tColumn, @IsDescending
END
--Tidy up
CLOSE ColumnCursor
DEALLOCATE ColumnCursor
--Delete the last comma
SET @Columns = LEFT(@Columns, LEN(@Columns) - 1)
END
--Recreate the constraint
SET @SQL = REPLACE(@SQL, '@Columns', @Columns)
PRINT @SQL
EXEC (@SQL)
PRINT('--Done
')
SET @SQL = ''
--Get the next TableName
FETCH NEXT FROM TableCursor
INTO @TableName
END
--Tidy up
CLOSE TableCursor
DEALLOCATE TableCursor
DROP TABLE #PKBackup
/* Handle exceptional tables here
* Replace indexes and foreign keys that were removed at the start
*/
SET NOCOUNT OFF