SQL Server 2005 drop column with constraints

后端 未结 13 985
一向
一向 2020-12-07 23:54

I have a column with a \"DEFAULT\" constraint. I\'d like to create a script that drops that column.

The problem is that it returns this error:

Msg 50         


        
相关标签:
13条回答
  • 2020-12-08 00:42

    I also think it's a shortcoming in SQL server to not have a cascading drop available. I worked my way around it by querying the system tables in the same way as other people described here:

    • INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE only lists foreign keys, primary keys and unique constraints.
    • The only way to look for default constraints is to look for them in sys.default_constraints.
    • what hasn't been mentioned here yet, is that indexes also make dropping a column fail, so you also need to drop all indexes that use your column before you can proceed with dropping a column.

    The resulting script is not pretty, but I put it in a stored procedure to be able to reuse it:

    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
    
    0 讨论(0)
  • 2020-12-08 00:44

    The answer from pvolders was just what I needed but it missed statistics which were causing and error. This is the same code, minus creating a stored procedure, plus enumerating statistics and dropping them. This is the best I could come up with so if there is a better way to determine what statistics need to be dropped please add.

    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
    
    0 讨论(0)
  • 2020-12-08 00:46

    This query finds default constraints for a given table. It aint pretty, I agree:

    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
    

    [EDIT] Updated per Julien N's comment

    0 讨论(0)
  • 2020-12-08 00:46
    > select CONSTRAINT_NAME from INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE
    > WHERE TABLE_NAME = '<tablename>' AND COLUMN_NAME = 'IsClosed'
    

    It's not the right solution as it is explained here : http://msdn.microsoft.com/en-us/library/aa175912.aspx that :

    Unfortunately, the name of the column default constraint isn't kept in the ANSI COLUMNS view, so you must go back to the system tables to find the name

    The only way I found to get the name of the DEFAULT constraint is this request :

    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'
    

    Am I the only one to find it crazy to be unable to delete easily a constraint that only concerns the columns I'm trying to drop ?
    I need to execute a request with 3 joins just to get the name...

    0 讨论(0)
  • 2020-12-08 00:46

    Just to build on Jeremy Stein's answer, I created a stored procedure for this, and set it up so it can be used to delete a column that has or does not have default constraints. It's not real efficient since it's querying sys.columns twice, but it works.

    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
    
    0 讨论(0)
  • 2020-12-08 00:47

    I just ran into this. You can delete the column with constraints using MSSQL design view. Right click on the column you want to be dropped (with or without constraints) and you are able to delete this without any problems. Ha.. I looked stupid already.

    0 讨论(0)
提交回复
热议问题