Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/sql-server-2008/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 使用主键更改列数据类型_Sql_Sql Server 2008_Primary Key - Fatal编程技术网

Sql 使用主键更改列数据类型

Sql 使用主键更改列数据类型,sql,sql-server-2008,primary-key,Sql,Sql Server 2008,Primary Key,我在80多个不同的表中有一个ReferenceID varchar(6)列。在分配ID的政府组织实施变更后,我需要将其扩展到整个db的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.table

我在80多个不同的表中有一个ReferenceID varchar(6)列。在分配ID的政府组织实施变更后,我需要将其扩展到整个db的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)
然后按如下方式编辑类型:

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,请记住,PK将取决于我们当前正在查看的表?

您需要将
ALTER
语句作为动态SQL执行:将该语句构建为SQL字符串,并将其传递给
sp_executesql
您需要在
ALTER表中显式指定
NOT NULL
。。。更改列
,否则默认为允许
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
当省略
notnull
时,会出现以下错误

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.

<> P>在编程方法中要考虑的两件事是,您需要暂时删除引用“代码”>引用ID 列的任何外键,并且确保(不包含“<代码> > NULL < /代码>”(非PK)。
ReferenceID
当前可为空的列。

编辑如果您的数据库混乱不堪,并且由于长达10年的开发(政府政策的变化足以导致“良好的数据库设计”)而混合使用varchar(6)和char(6)列,则需要此解决方案最终崩溃。) 结束编辑

对于那些说我必须放弃并重新创建PK的人,你是对的。索引和外键也需要删除和重新创建

幸运的是,索引和FK的数量是可以管理的,所以我将它们作为“例外”处理,并在脚本开始时一次删除一个,然后在脚本结束时一次重新添加一个(请参见下面的/**/中的两个部分)

然后,SQL脚本的主体将有关FK的完整细节提示到临时表中,然后循环遍历每个表名,删除FK,更改数据类型,重新添加FK

组装好的SQL字符串打印在下面的脚本中。如果你打算重复使用(没有提供任何保证,等等,诸如此类),请将其注释掉,以使执行时间减少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
将NOCOUNT设置为ON
/*在这里处理特殊的表格
*删除索引和外键
*--大量“如果存在……更改表删除约束等”。
*/
--声明变量
声明@SQL VARCHAR(8000)
声明@TableName VARCHAR(512)
声明@ConstraintName VARCHAR(512)
声明@tColumn VARCHAR(512)
声明@Columns VARCHAR(8000)
声明@IsDescending位
--设置临时表
挑选
tbl.[schema_id],
tbl.name作为表名,
i、 名称为IndexName,
i、 类型_desc,
c、 [专栏],
c、 关键点(序数),
c、 是_desc,
i、 [对象id],
s、 没有重新计算,
i、 [忽略重复键],
i、 [允许行锁定],
i、 [允许页面锁定],
i、 [填充系数],
dsi.type,
dsi.name作为DataSpaceName
进入#PKBackup
从…起
sys.tables作为tbl
内部连接sys.index作为i
在(
i、 索引id>0
i.is_=0
)
和(i.[object\u id]=tbl.[object\u id])
内连接(
挑选
ic.[对象id],
c、 [姓名][栏目],
ic.is_下降键[is_下降],
ic.key_序数
从…起
sys.index_列ic
内连接
系统索引i
在…上
i、 [object\u id]=ic.[object\u id]
及
i、 索引id=1
及
ic.index_id=1
内连接
系统表t
在…上
t、 [object\u id]=ic.[object\u id]
内连接
sys.c列
在…上
c、 [object\u id]=t.[object\u id]
及
c、 column\u id=ic.column\u id
)AS c
在c.[object\u id]=i.[object\u id]上
左外连接
sys.key_约束为k
在…上
k、 父对象id=i.[对象id]
及
k、 唯一索引id=i.index\U id
左外连接
sys.data_空间作为dsi
在…上
dsi.data\u space\u id=i.data\u space\u id
左外连接
席
在…上
席[物]
及
X.NoxxIdID.I.索引XIDID
左外连接
sys.stats为s
在…上
s、 stats\u id=i.index\u id
及
s、 [object\u id]=i.[object\u id]
哪里
k、 类型='PK'
将TableCursor游标声明为本地只读
选择t.name作为TableName
来自sys.c列
在c.object\u id=t.object\u id上连接sys.tables t
哪里
c、 名称='ReferenceID'
打开表格光标
从TableCursor获取下一个
转换为@TableName
而@@FETCH\u STATUS=0
开始
打印('--更新'+@TableName+'…'))
选择@ConstraintName=PK.CONSTRAINT\u NAME
从…起
信息\u SCHEMA.TABLE\u约束PK
哪里
PK.TABLE_NAME=@TableName
及
PK.CONSTRAINT_TYPE='主键'
--放弃约束
--有些表没有定义PK,只有定义PK时才执行下一位
如果(从#PKBackup PK中选择COUNT(*),其中PK.TableName=@TableName)>0
开始
SET@SQL='ALTER TABLE@TableName DROP CONSTRAINT@ConstraintName'
设置@SQL=REPLACE(@SQ