【问题标题】:SQL Server 2005 drop column with constraintsSQL Server 2005 删除带有约束的列
【发布时间】:2008-11-24 18:36:30
【问题描述】:

我有一列带有“DEFAULT”约束。我想创建一个删除该列的脚本。

问题是它返回这个错误:

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

我找不到删除列及其所有相关约束的简单方法(仅找到查看系统表的大脚本......必须(!!)是一种“好”的方法来做到这一点。 )

由于默认约束的名称是随机生成的,我不能按名称删除它。


更新
约束类型为“DEFAULT”。

我看到了你们提出的解决方案,但我发现它们都非常“肮脏”……你不觉得吗? 我不知道是 Oracle 还是 MySQL,但可以这样做:

DROP COLUMN xxx CASCADE CONSTRAINTS 

它放弃了所有相关的约束...... 或者至少它会自动删除映射到该列的约束(至少检查约束!)

MSSQL 中没有类似的东西吗?

【问题讨论】:

    标签: sql-server sql-server-2005


    【解决方案1】:

    这是一个脚本,它将删除列及其默认约束。适当替换MYTABLENAMEMYCOLUMNNAME

    declare @constraint_name sysname, @sql nvarchar(max)
    
    select @constraint_name = name 
    from sys.default_constraints 
    where parent_object_id = object_id('MYTABLENAME')
    AND type = 'D'
    AND parent_column_id = (
        select column_id 
        from sys.columns 
        where object_id = object_id('MYTABLENAME')
        and name = 'MYCOLUMNNAME'
        )
    
    set @sql = N'alter table MYTABLENAME drop constraint ' + @constraint_name
    exec sp_executesql @sql
    
    alter table MYTABLENAME drop column MYCOLUMNNAME
    
    go
    

    【讨论】:

    • 不错,这看起来像我所追求的。
    • 我需要做'alter table MYTABLENAME drop column MYCOLUMNNAME'
    • @Bill 谢谢!我已经修好了。
    • 使用子查询而不是连接是否有任何实际原因?
    • 没有。它只是反映了我的大脑处理问题的方式。
    【解决方案2】:

    此查询查找给定表的默认约束。不漂亮,我同意:

    select 
        col.name, 
        col.column_id, 
        col.default_object_id, 
        OBJECTPROPERTY(col.default_object_id, N'IsDefaultCnst') as is_defcnst, 
        dobj.name as def_name
    from sys.columns col 
        left outer join sys.objects dobj 
            on dobj.object_id = col.default_object_id and dobj.type = 'D' 
    where col.object_id = object_id(N'dbo.test') 
    and dobj.name is not null
    

    [编辑] 根据 Julien N 的评论更新

    【讨论】:

    • 您的回答是正确的,但它与我的不同。您从一个特定的表中获得约束,我的获得所有约束。两件事:语法错误(删除之前的逗号),您应该在 WHERE 子句中添加“and dobj.name is not null”以删除不相关的结果。
    【解决方案3】:

    也许它可以帮助更多:

    declare @tablename nvarchar(200)
    declare @colname nvarchar(200)
    declare @default sysname, @sql nvarchar(max)
    
    set @tablename = 'your table'
    set @colname = 'column to drop'
    
    select @default = name 
    from sys.default_constraints 
    where parent_object_id = object_id(@tablename)
    AND type = 'D'
    AND parent_column_id = (
        select column_id 
        from sys.columns 
        where object_id = object_id(@tablename)
        and name = @colname 
        )
    
    set @sql = N'alter table ' + @tablename + ' drop constraint ' + @default
    exec sp_executesql @sql
    
    set @sql = N'alter table ' + @tablename + ' drop column ' + @colname
    exec sp_executesql @sql
    

    只需要设置@tablename 和@colname 变量来删除列。

    【讨论】:

    【解决方案4】:
    > select CONSTRAINT_NAME from INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE
    > WHERE TABLE_NAME = '<tablename>' AND COLUMN_NAME = 'IsClosed'
    

    这不是正确的解决方案,因为它在这里解释:http://msdn.microsoft.com/en-us/library/aa175912.aspx 那:

    不幸的是,列默认约束的名称没有保存在 ANSI COLUMNS 视图中,因此您必须返回系统表才能找到名称

    我发现获取 DEFAULT 约束名称的唯一方法是这个请求:

    select  
        t_obj.name              as TABLE_NAME
        ,c_obj.name             as CONSTRAINT_NAME
        ,col.name               as COLUMN_NAME
    
    from    sysobjects  c_obj
    join    sysobjects  t_obj on c_obj.parent_obj = t_obj.id  
    join    sysconstraints con on c_obj.id  = con.constid
    join    syscolumns  col on t_obj.id = col.id
                and con.colid = col.colid
    where
        c_obj.xtype = 'D'
    

    只有我一个人发现它疯狂无法轻松删除仅涉及我要删除的列的约束吗?
    我需要执行一个包含 3 个连接的请求才能获得名称...

    【讨论】:

    • 查看我的答案以获得更简洁的查询
    • 就像字符串拆分/连接一样,这是另一种情况,每个人都必须实现自己的笨拙的解决方法,而 SQL Server 本身应该具有该功能。
    【解决方案5】:

    我也认为 SQL Server 的一个缺点是没有可用的级联删除。我通过以与此处描述的其他人相同的方式查询系统表来解决它:

    • INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE 仅列出外键、主键和唯一约束。
    • 查找默认约束的唯一方法是在 sys.default_constraints 中查找它们。
    • 这里还没有提到的是,索引 也会使删除列失败,因此您还需要删除所有使用您的列的索引,然后才能继续删除列。

    生成的脚本并不漂亮,但我把它放在一个存储过程中以便能够重用它:

    CREATE PROCEDURE DropColumnCascading @tablename nvarchar(500), @columnname nvarchar(500)
    AS
    
    SELECT CONSTRAINT_NAME, 'C' AS type
    INTO #dependencies
    FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE WHERE TABLE_NAME = @tablename AND COLUMN_NAME = @columnname
    
    INSERT INTO #dependencies
    select d.name, 'C'
    from sys.default_constraints d
    join sys.columns c ON c.column_id = d.parent_column_id AND c.object_id = d.parent_object_id
    join sys.objects o ON o.object_id = d.parent_object_id
    WHERE o.name = @tablename AND c.name = @columnname
    
    INSERT INTO #dependencies
    SELECT i.name, 'I'
    FROM sys.indexes i
    JOIN sys.index_columns ic ON ic.index_id = i.index_id and ic.object_id=i.object_id
    JOIN sys.columns c ON c.column_id = ic.column_id and c.object_id=i.object_id
    JOIN sys.objects o ON o.object_id = i.object_id
    where o.name = @tableName AND i.type=2 AND c.name = @columnname AND is_unique_constraint = 0
    
    DECLARE @dep_name nvarchar(500)
    DECLARE @type nchar(1)
    
    DECLARE dep_cursor CURSOR
    FOR SELECT * FROM #dependencies
    
    OPEN dep_cursor
    
    FETCH NEXT FROM dep_cursor 
    INTO @dep_name, @type;
    
    DECLARE @sql nvarchar(max)
    
    WHILE @@FETCH_STATUS = 0
    BEGIN
        SET @sql = 
            CASE @type
                WHEN 'C' THEN 'ALTER TABLE [' + @tablename + '] DROP CONSTRAINT [' + @dep_name + ']'
                WHEN 'I' THEN 'DROP INDEX [' + @dep_name + '] ON dbo.[' + @tablename + ']'
            END
        print @sql
        EXEC sp_executesql @sql
        FETCH NEXT FROM dep_cursor 
        INTO @dep_name, @type;
    END
    
    DEALLOCATE dep_cursor
    
    DROP TABLE #dependencies
    
    SET @sql = 'ALTER TABLE [' + @tablename + '] DROP COLUMN [' + @columnname + ']'
    
    print @sql
    EXEC sp_executesql @sql
    

    【讨论】:

      【解决方案6】:

      pvolders 的答案正是我所需要的,但它错过了导致错误的统计数据。这是相同的代码,减去创建存储过程,加上枚举统计信息并删除它们。这是我能想到的最好的方法,所以如果有更好的方法来确定需要删除哪些统计数据,请添加。

      DECLARE @tablename nvarchar(500), 
              @columnname nvarchar(500)
      
      SELECT  @tablename = 'tblProject',
              @columnname = 'CountyKey'
      
      
      SELECT CONSTRAINT_NAME, 'C' AS type
      INTO #dependencies
      FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE WHERE TABLE_NAME = @tablename AND COLUMN_NAME = @columnname
      
      INSERT INTO #dependencies
      select d.name, 'C'
      from sys.default_constraints d
      join sys.columns c ON c.column_id = d.parent_column_id AND c.object_id = d.parent_object_id
      join sys.objects o ON o.object_id = d.parent_object_id
      WHERE o.name = @tablename AND c.name = @columnname
      
      INSERT INTO #dependencies
      SELECT i.name, 'I'
      FROM sys.indexes i
      JOIN sys.index_columns ic ON ic.index_id = i.index_id and ic.object_id=i.object_id
      JOIN sys.columns c ON c.column_id = ic.column_id and c.object_id=i.object_id
      JOIN sys.objects o ON o.object_id = i.object_id
      where o.name = @tableName AND i.type=2 AND c.name = @columnname AND is_unique_constraint = 0
      
      INSERT INTO #dependencies
      SELECT s.NAME, 'S'
      FROM sys.stats AS s
      INNER JOIN sys.stats_columns AS sc 
          ON s.object_id = sc.object_id AND s.stats_id = sc.stats_id
      INNER JOIN sys.columns AS c 
          ON sc.object_id = c.object_id AND c.column_id = sc.column_id
      WHERE s.object_id = OBJECT_ID(@tableName)
      AND c.NAME = @columnname
      AND s.NAME LIKE '_dta_stat%'
      
      DECLARE @dep_name nvarchar(500)
      DECLARE @type nchar(1)
      
      DECLARE dep_cursor CURSOR
      FOR SELECT * FROM #dependencies
      
      OPEN dep_cursor
      
      FETCH NEXT FROM dep_cursor 
      INTO @dep_name, @type;
      
      DECLARE @sql nvarchar(max)
      
      WHILE @@FETCH_STATUS = 0
      BEGIN
          SET @sql = 
              CASE @type
                  WHEN 'C' THEN 'ALTER TABLE [' + @tablename + '] DROP CONSTRAINT [' + @dep_name + ']'
                  WHEN 'I' THEN 'DROP INDEX [' + @dep_name + '] ON dbo.[' + @tablename + ']'
                  WHEN 'S' THEN 'DROP STATISTICS [' + @tablename + '].[' + @dep_name + ']'
              END
          print @sql
          EXEC sp_executesql @sql
          FETCH NEXT FROM dep_cursor 
          INTO @dep_name, @type;
      END
      
      DEALLOCATE dep_cursor
      
      DROP TABLE #dependencies
      
      SET @sql = 'ALTER TABLE [' + @tablename + '] DROP COLUMN [' + @columnname + ']'
      
      print @sql
      EXEC sp_executesql @sql
      

      【讨论】:

        【解决方案7】:

        您可以通过查询 information_schema 系统视图来获取约束名称。

        select CONSTRAINT_NAME from INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE WHERE TABLE_NAME = '<tablename>' AND COLUMN_NAME = 'IsClosed'
        

        【讨论】:

        • 这不返回默认约束,如下所述
        【解决方案8】:

        只是为了建立在 Jeremy Stein 的回答之上,我为此创建了一个存储过程,并将其设置为可用于删除具有或不具有默认约束的列。由于它查询 sys.columns 两次,因此效率并不高,但它确实有效。

        CREATE PROCEDURE [dbo].[RemoveColumnWithDefaultConstraints] 
            -- Add the parameters for the stored procedure here
            @tableName nvarchar(max), 
            @columnName nvarchar(max)
        AS
        BEGIN
            -- SET NOCOUNT ON added to prevent extra result sets from
            -- interfering with SELECT statements.
            SET NOCOUNT ON;
        
            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)
        
            IF EXISTS(SELECT * FROM sys.columns WHERE Name = @columnName  
                AND Object_ID = Object_ID(@tableName))
                EXEC('ALTER TABLE ' + @tableName + ' DROP COLUMN ' + @columnName)       
        END
        GO
        

        【讨论】:

        • 隐形方法获胜!
        【解决方案9】:

        查找约束的名称或使用 MSSQL 设计视图并不总是一种选择。我目前想制作一个脚本来删除带有约束的列。使用名称不是一个选项,因为名称是生成的,我想在不同的环境 Dev/Sys/Prod/etc 中使用脚本。然后无法使用该名称,因为每个环境的约束名称会有所不同。我可能需要查看系统表,但我同意应该有一个更简单的选项。

        【讨论】:

          【解决方案10】:

          我相信在删除列之前明确删除约束是一种“更干净”的解决方案。这样,您就不会放弃您可能不知道的约束。如果删除仍然失败,您知道还有其他限制。我喜欢准确地控制我的数据库发生的事情。

          另外,为 drop 编写脚本明确地保证了脚本,并希望结果可以完全按照您想要的方式重复。

          【讨论】:

          • “你不会放弃你可能不知道的约束” - 我同意。但不适用于仅适用于已删除列的约束...我想我必须请求数据库知道约束的名称才能删除它...
          【解决方案11】:

          随机生成是什么意思?您可以在 management studio 中或通过 sys.tables 视图查找特定列的约束,并查找名称。

          然后,您可以更改脚本以在删除列之前删除约束。这是什么类型的约束?如果是外键约束,请确保这样做不会损害数据库中的数据完整性。

          【讨论】:

            【解决方案12】:

            我刚碰到这个。您可以使用 MSSQL 设计视图删除带有约束的列。右键单击要删除的列(有或没有约束),您可以毫无问题地删除它。哈..我看起来已经很愚蠢了。

            【讨论】:

            • 是的,您可以通过企业管理器将其删除。目标是通过脚本来完成。我无法直接访问 sql server 实例。
            【解决方案13】:

            只需为表格生成脚本。在那里您可以找到所有约束的名称。

            【讨论】:

              猜你喜欢
              • 2011-02-05
              • 2011-01-11
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多