【问题标题】:How to drop column with constraint?如何删除有约束的列?
【发布时间】:2012-01-28 08:12:53
【问题描述】:

如何删除 SQL Server 2008 中具有默认约束的列?

我的查询是

alter table tbloffers
drop column checkin

我遇到了错误

ALTER TABLE DROP COLUMN 签入失败,因为一个或多个对象访问此列。

谁能更正我的查询以删除有约束的列?

【问题讨论】:

  • 可能有其他表对该表的一些引用导致此错误。
  • 对于遇到此问题的新手,请查看my answer below,如果它适合您,它比其他一些解决方案要简单得多。
  • 我已经列出了我的问题和答案here

标签: sql-server sql-server-2008


【解决方案1】:

首先你应该删除有问题的DEFAULT constraint,然后你可以删除列

alter table tbloffers drop constraint [ConstraintName]
go

alter table tbloffers drop column checkin

但错误可能由其他原因出现 - 例如用户定义的函数或为它们设置了SCHEMABINDING 选项的视图。

统一更新: 完全自动删除约束脚本:

DECLARE @sql NVARCHAR(MAX)
WHILE 1=1
BEGIN
    SELECT TOP 1 @sql = N'alter table tbloffers drop constraint ['+dc.NAME+N']'
    from sys.default_constraints dc
    JOIN sys.columns c
        ON c.default_object_id = dc.object_id
    WHERE 
        dc.parent_object_id = OBJECT_ID('tbloffers')
    AND c.name = N'checkin'
    IF @@ROWCOUNT = 0 BREAK
    EXEC (@sql)
END

【讨论】:

  • 感谢自动化脚本。像魅力一样工作!
  • 非常感谢 - 你为我节省了很多时间。我已将我提出的问题链接到此处
【解决方案2】:

在此处使用此查询查找默认约束:

SELECT
    df.name 'Constraint Name' ,
    t.name 'Table Name',
    c.NAME 'Column Name'
FROM sys.default_constraints df
INNER JOIN sys.tables t ON df.parent_object_id = t.object_id
INNER JOIN sys.columns c ON df.parent_object_id = c.object_id AND df.parent_column_id = c.column_id

这将为您提供默认约束的名称,以及表和列名称。

当您获得该信息后,您需要首先删除默认约束:

ALTER TABLE dbo.YourTable
DROP CONSTRAINT name-of-the-default-constraint-here

然后你可以删除列

ALTER TABLE dbo.YourTable DROP COLUMN YourColumn

【讨论】:

  • 不必按顺序进行。你可以同时做这两件事。
  • @MartinSmith:好的,太好了 - 感谢分享!我没有意识到这种可能性——你每天都会学到新东西! :-)
  • 谁能提供一个如何结合这两个语句的例子。我需要一些想法,例如:ALTER TABLE table DROP CONSTRAINT DF_XY DROP COLUMN XY 不幸的是,此语句的语法不正确
  • @My-Name-Is:如果您查看 Martin 的答案,则需要在两个 DROP 命令之间添加 逗号 以使其正常工作
【解决方案3】:

您也可以在单个语句中而不是单独删除列及其约束。

CREATE TABLE #T
  (
     Col1 INT CONSTRAINT UQ UNIQUE CONSTRAINT CK CHECK (Col1 > 5),
     Col2 INT
  )

ALTER TABLE #T DROP CONSTRAINT UQ , 
                    CONSTRAINT CK, 
                    COLUMN Col1


DROP TABLE #T 

下面是一些动态 SQL,它将查找依赖检查约束和默认约束的名称并将它们与列一起删除

(但不包括其他可能的列依赖关系,例如外键、唯一和主键约束、计算列、索引)

CREATE TABLE [dbo].[TestTable]
(
A INT DEFAULT '1' CHECK (A=1),
B INT,
CHECK (A > B)
)

GO

DECLARE @TwoPartTableNameQuoted nvarchar(500) = '[dbo].[TestTable]',
        @ColumnNameUnQuoted sysname = 'A',
        @DynSQL NVARCHAR(MAX);

SELECT @DynSQL =
     'ALTER TABLE ' + @TwoPartTableNameQuoted + ' DROP' + 
      ISNULL(' CONSTRAINT ' + QUOTENAME(OBJECT_NAME(c.default_object_id)) + ',','') + 
      ISNULL(check_constraints,'') + 
      '  COLUMN ' + QUOTENAME(@ColumnNameUnQuoted)
FROM   sys.columns c
       CROSS APPLY (SELECT ' CONSTRAINT ' + QUOTENAME(OBJECT_NAME(referencing_id)) + ','
                    FROM   sys.sql_expression_dependencies
                    WHERE  referenced_id = c.object_id
                           AND referenced_minor_id = c.column_id
                           AND OBJECTPROPERTYEX(referencing_id, 'BaseType') = 'C'
                    FOR XML PATH('')) ck(check_constraints)
WHERE  c.object_id = object_id(@TwoPartTableNameQuoted)
       AND c.name = @ColumnNameUnQuoted;

PRINT @DynSQL;
EXEC (@DynSQL); 

【讨论】:

  • 这需要您知道约束的名称。如果它们在表创建期间没有被命名,它们会得到一个自动生成的名称。
  • @Joey - 没有语法可以在不知道名称的情况下删除约束。这是DROP CONSTRAINT see grammar 的必需参数如果您没有明确命名约束,那么您必须查找 SQL Server 为其生成的名称,例如根据马克的回答。但是发现你仍然可以同时删除约束和列。
  • 不错的代码,我需要一次删除多个约束,而不是列。你的改变成功了。谢谢!!
【解决方案4】:

这是另一种删除具有未知名称的默认约束的方法,而无需先运行单独的查询来获取约束名称:

DECLARE @ConstraintName nvarchar(200)
SELECT @ConstraintName = Name FROM SYS.DEFAULT_CONSTRAINTS
WHERE PARENT_OBJECT_ID = OBJECT_ID('__TableName__')
AND PARENT_COLUMN_ID = (SELECT column_id FROM sys.columns
                        WHERE NAME = N'__ColumnName__'
                        AND object_id = OBJECT_ID(N'__TableName__'))
IF @ConstraintName IS NOT NULL
EXEC('ALTER TABLE __TableName__ DROP CONSTRAINT ' + @ConstraintName)

【讨论】:

  • 一个极好的答案,谢谢。不过,我确实也赞成上述答案,只是出于在决定放弃之前先进行选择和检查的畏缩旧习惯。
  • 确实很好的答案。为了方便/将来使用,我制作了一个存储过程:pastebin.com/2CeXZDh2
  • 很好的答案,但当有多个约束绑定到一列时仍然缺少方法。一些类似于 @Digs 的包含循环的帖子的存储过程可能是 5 星答案
  • 太棒了!非常感谢
【解决方案5】:

我也一样:

ALTER TABLE DROP COLUMN 失败,因为一个或多个对象访问此列消息。

我的专栏有一个需要先删除的索引。使用 sys.indexes 成功了:

DECLARE @sql VARCHAR(max)

SELECT @sql = 'DROP INDEX ' + idx.NAME + ' ON tblName'
FROM sys.indexes idx
INNER JOIN sys.tables tbl ON idx.object_id = tbl.object_id
INNER JOIN sys.index_columns idxCol ON idx.index_id = idxCol.index_id
INNER JOIN sys.columns col ON idxCol.column_id = col.column_id
WHERE idx.type <> 0
    AND tbl.NAME = 'tblName'
    AND col.NAME = 'colName'

EXEC sp_executeSql @sql
GO

ALTER TABLE tblName
DROP COLUMN colName

【讨论】:

    【解决方案6】:

    以下对我来说适用于 SQL Azure 后端(使用 SQL Server Management Studio),所以 YMMV,但是,如果它适用于你,它比其他解决方案更简单。

    ALTER TABLE MyTable
        DROP CONSTRAINT FK_MyColumn
        CONSTRAINT DK_MyColumn
        -- etc...
        COLUMN MyColumn
    GO
    

    【讨论】:

      【解决方案7】:

      我已经将脚本稍微更新到我的 SQL Server 版本

      DECLARE @sql nvarchar(max)
      
      SELECT @sql = 'ALTER TABLE `table_name` DROP CONSTRAINT ' + df.NAME 
      FROM sys.default_constraints df
        INNER JOIN sys.tables t ON df.parent_object_id = t.object_id
        INNER JOIN sys.columns c ON df.parent_object_id = c.object_id AND df.parent_column_id = c.column_id
      where t.name = 'table_name' and c.name = 'column_name'
      
      EXEC sp_executeSql @sql
      GO
      
      ALTER TABLE table_name
        DROP COLUMN column_name;
      
      

      【讨论】:

        【解决方案8】:

        阻止删除列的不仅仅是默认约束,有时索引也会阻止您删除约束。 因此,我编写了一个程序,该程序将删除列上的任何索引或约束,并在最后删除它自己的列。

        IF OBJECT_ID ('ADM_delete_column', 'P') IS NOT NULL
           DROP procedure ADM_delete_column;
        GO
        
        CREATE procedure ADM_delete_column
            @table_name_in  nvarchar(300)
        ,   @column_name_in nvarchar(300)
        AS 
        BEGIN
            /*  Author: Matthis (matthis@online.ms at 2019.07.20)
                License CC BY (creativecommons.org)
                Desc:   Administrative procedure that drops columns at MS SQL Server
                        - if there is an index or constraint on the column 
                            that will be dropped in advice
                        => input parameters are TABLE NAME and COLUMN NAME as STRING
            */
            SET NOCOUNT ON
        
            --drop index if exist (search first if there is a index on the column)
            declare @idx_name VARCHAR(100)
            SELECT  top 1 @idx_name = i.name
            from    sys.tables t
            join    sys.columns c
            on      t.object_id = c.object_id
            join    sys.index_columns ic
            on      c.object_id = ic.object_id
            and     c.column_id = ic.column_id
            join    sys.indexes i
            on      i.object_id = ic.object_id
            and     i.index_id = ic.index_id
            where   t.name like @table_name_in
            and     c.name like @column_name_in
            if      @idx_name is not null
            begin 
                print concat('DROP INDEX ', @idx_name, ' ON ', @table_name_in)
                exec ('DROP INDEX ' + @idx_name + ' ON ' + @table_name_in)
            end
        
            --drop fk constraint if exist (search first if there is a constraint on the column)
            declare @fk_name VARCHAR(100)
            SELECT  top 1 @fk_name = CONSTRAINT_NAME 
            from    INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE
            where   TABLE_NAME like @table_name_in
            and     COLUMN_NAME like @column_name_in
            if      @fk_name is not null
            begin 
                print concat('ALTER TABLE ', @table_name_in, ' DROP CONSTRAINT ', @fk_name)
                exec ('ALTER TABLE ' + @table_name_in + ' DROP CONSTRAINT ' + @fk_name)
            end
        
            --drop column if exist
            declare @column_name VARCHAR(100)
            SELECT  top 1 @column_name = COLUMN_NAME 
            FROM    INFORMATION_SCHEMA.COLUMNS 
            WHERE   COLUMN_NAME like concat('%',@column_name_in,'%')
            if  @column_name is not null
            begin 
                print concat('ALTER TABLE ', @table_name_in, ' DROP COLUMN ', @column_name)
                exec ('ALTER TABLE ' + @table_name_in + ' DROP COLUMN ' + @column_name)
            end
        end;
        GO
        
        
        --to run the procedure use this execute and fill the parameters 
        execute ADM_delete_column 
            @table_name_in  = ''
        ,   @column_name_in = ''
            ;
        

        【讨论】:

          【解决方案9】:

          根据前面的答案,我将其添加为存储过程,以简化在附加约束时删除列的过程

          CREATE OR ALTER PROC DROP_COLUMN(@TableName nvarchar(200), @ColumnName nvarchar(200))
          AS 
          BEGIN 
              DECLARE @ConstraintName nvarchar(200)
              SELECT @ConstraintName = Name FROM SYS.DEFAULT_CONSTRAINTS
              
              WHERE PARENT_OBJECT_ID = OBJECT_ID(@TableName)
              AND PARENT_COLUMN_ID = (SELECT column_id FROM sys.columns
                                      WHERE NAME = @ColumnName
                                      AND object_id = OBJECT_ID(@TableName))
              IF @ConstraintName IS NOT NULL
              EXEC('ALTER TABLE '+@TableName+' DROP CONSTRAINT ' + @ConstraintName)
              EXEC('ALTER TABLE '+@TableName+' DROP COLUMN IF EXISTS ' + @ColumnName)
          END
          
          GO 
          --example:
          EXEC DROP_COLUMN N'VEHICLES', N'SCMT'
          EXEC DROP_COLUMN N'VEHICLES', N'SSC'
          EXEC DROP_COLUMN N'VEHICLES', N'RS'
          EXEC DROP_COLUMN N'VEHICLES', N'RCEC'
           
          DROP PROCEDURE IF EXISTS DROP_COLUMN 
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-04-16
            • 2014-09-12
            • 2013-11-05
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多