9

80 以上の異なるテーブルに ReferenceID varchar(6) 列があります。ID を割り当てる政府機関によって実装された変更に続いて、データベース全体でこれを varchar(8) に拡張する必要があります。

次のように、テーブル名を取得するためにカーソルを宣言したいと考えていました。

DECLARE @TableName AS VARCHAR(200)
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

次に、タイプを次のように編集します。

ALTER TABLE @TableName ALTER COLUMN ReferenceID VARCHAR(8)

列が一部のテーブルの主キーの一部であるため、これは失敗します (また、PK に含まれる列はテーブルごとに異なります)。

テーブルごとに各PKを手動で削除して再作成する必要はありません。

カーソル内で、データ型を変更する前に PK を無効にしてから再度有効にする方法、またはデータ型を変更する両側で PK を削除して再作成する方法はありますか? '現在見ていますか?

4

4 に答える 4

7

NOT NULLで明示的に指定する必要があります。ALTER TABLE ... ALTER COLUMNそれ以外の場合は、デフォルトで許可されNULLます。これは PK 列では許可されていません。

以下は正常に動作します。

CREATE TABLE p
(
ReferenceID VARCHAR(6) NOT NULL PRIMARY KEY
)

INSERT INTO p VALUES ('AAAAAA')

ALTER TABLE p ALTER COLUMN ReferenceID VARCHAR(8) NOT NULL

NOT NULL省略すると、次のエラーが発生します

Msg 5074, Level 16, State 1, Line 1
The object 'PK__p__E1A99A792180FB33' is dependent on column 'ReferenceID'.
Msg 4922, Level 16, State 9, Line 1
ALTER TABLE ALTER COLUMN ReferenceID failed because one or more objects access this column.

プログラムによるアプローチで考慮すべき点は、列を一時的に参照する外部キーを削除する必要あることと、現在null 許容のfor (Non PK)列をReferenceID含めないようにする必要があることです。NOT NULLReferenceID

于 2012-06-20T15:23:44.377 に答える
3

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
于 2012-06-22T13:39:31.070 に答える
0

ステートメントを動的 SQL として実行する必要がありALTERます。ステートメントを SQL 文字列として作成し、 に渡しますsp_executesql

于 2012-06-20T11:43:40.533 に答える
0

私の 30 年以上にわたるデータベースの経験から、1 つの不変の点は、データ要件の変化に応じて、使用しているデータベースの構造を継続的に変更する必要があるということです。さらに、自動インクリメントの主キーが最適でない例がたくさんあります。特に、データベースを使用するプログラムが実行されているときに、DBMS (SQL Server など) を介してデータに意味のある方法で直接アクセスできるようにしたい場合は特にそうです。利用できない。データベース管理の大きな欠点の 1 つは、プログラムが停止したときにデータベースが通用しないことです。これは、長期的なデータ管理の原則に完全に反するものです。

したがって、主キー フィールドのサイズを簡単に変更できないのは、データベースの設計が不十分なためではありません。データベースを処理する DBMS および SQL ツールが著しく不十分であり、実際の理解ではなく理論的な知識を持つプログラマによって明確に設計されているためです。現実世界の。このようなプログラミングの欠陥の他の例としては、1 ではなく 0 から始まる配列インデックス (インデックスからカウントに切り替えるために 1 を加算または減算する必要があるために発生するエラーの数が非常に多い)、数値変数が null 値をネイティブに処理できないなどがあります。 . データベース構造の変更が、いわゆるデータベース設計の悪さから生じるのではなく、主流の必要性と見なされる日が来ることを楽しみにしています。

于 2014-05-06T04:11:28.353 に答える