¿Cómo soltar una columna con restricción?

138

¿Cómo soltar una columna que tiene la restricción predeterminada en SQL Server 2008?

Mi consulta es

alter table tbloffers
drop column checkin

Me estoy poniendo debajo del error

El registro ALTER TABLE DROP COLUMN falló porque uno o más objetos acceden a esta columna.

¿Alguien puede corregir mi consulta para colocar una columna con restricción?

Robin Michael Poothurai
fuente
puede haber algunas referencias a esta tabla de otras tablas que están causando este error.
Pankaj Upadhyay
Para los recién llegados que se topan con esto, mire mi respuesta a continuación , si funciona para usted, es mucho más simple que algunas de las otras soluciones.
BrainSlugs83
He enumerado mi pregunta y respuesta aquí
Akash Yellappa

Respuestas:

233

Primero debe descartar la problemática DEFAULT constraint, luego puede descartar la columna

alter table tbloffers drop constraint [ConstraintName]
go

alter table tbloffers drop column checkin

Pero el error puede aparecer por otros motivos, por ejemplo, la función o vista definida por el usuario con la SCHEMABINDINGopción establecida para ellos.

UPD: secuencia de comandos de eliminación de restricciones completamente automatizada:

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
Oleg Dok
fuente
1
Gracias por el script automatizado. ¡Funciona de maravilla!
kanadianDri3
Muchas gracias, me ahorraste mucho tiempo. He vinculado la pregunta que hice aquí
Akash Yellappa
130

Aquí hay otra forma de eliminar una restricción predeterminada con un nombre desconocido sin tener que ejecutar primero una consulta separada para obtener el nombre de la restricción:

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)
Chris Halcrow
fuente
Una excelente respuesta, gracias. Sin embargo, también voté por la respuesta anterior, solo por ese viejo y vacilante hábito de SELECCIONAR e inspeccionar primero antes de decidir abandonar.
noogrub
77
Gran respuesta de hecho. Hice un procedimiento almacenado para su conveniencia / uso futuro: pastebin.com/2CeXZDh2
Excava el
Excelente respuesta pero aún falta enfoque cuando hay más de una restricción vinculada a una columna. Algunos procedimientos almacenados similares a la publicación de @Digs con el bucle incluido podrían tener una respuesta de 5 estrellas
YeinCM-Qva
27

También puede soltar la columna y sus restricciones en una sola declaración en lugar de hacerlo individualmente.

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 

A continuación se muestra un poco de SQL dinámico que buscará los nombres de las restricciones de verificación dependientes y las restricciones predeterminadas y las soltará junto con la columna

(pero no otras posibles dependencias de columna como claves foráneas, restricciones de clave única y primaria, columnas calculadas, índices)

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); 
Martin Smith
fuente
Sin embargo, esto requiere que sepas el nombre de la restricción. Si no se han nombrado durante la creación de la tabla, obtienen un nombre generado automáticamente.
Joey
1
@Joey: no hay sintaxis para eliminar restricciones sin conocer el nombre. Es un argumento obligatorio para DROP CONSTRAINT ver la gramática. Si no nombra las restricciones explícitamente, tendrá que buscar el nombre que SQL Server generó, por ejemplo, según la respuesta de Marc. Pero habiendo descubierto eso, aún puede soltar la restricción y la columna al mismo tiempo.
Martin Smith
Buen código, necesitaba soltar múltiples restricciones a la vez, pero no la columna. Tu alter hizo el truco. ¡¡Gracias!!
htm11h
26

Encuentre la restricción predeterminada con esta consulta aquí:

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

Esto le da el nombre de la restricción predeterminada, así como el nombre de la tabla y la columna.

Cuando tenga esa información, primero debe eliminar la restricción predeterminada:

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

y luego puedes soltar la columna

ALTER TABLE dbo.YourTable DROP COLUMN YourColumn
marc_s
fuente
2
No tiene que hacerse secuencialmente. Puedes hacer ambas cosas al mismo tiempo.
Martin Smith
1
@ MartinSmith: OK, genial, ¡gracias por compartir! No estaba al tanto de esta posibilidad: ¡aprendes algo nuevo todos los días! :-)
marc_s
¿Puede alguien proporcionar un ejemplo de cómo combinar estas dos declaraciones? Necesito algo como: ALTER TABLE table DROP CONSTRAINT DF_XY DROP COLUMN XYDesafortunadamente, la sintaxis de esta declaración no es correcta
My-Name-Is
1
@ My-Name-Is: si revisa la respuesta de Martin, debe poner una coma entre los dos DROPcomandos para que esto funcione
marc_s
3

Lo siguiente funcionó para mí contra un back-end de SQL Azure (usando SQL Server Management Studio), por lo que YMMV, pero, si funciona para usted, es muuuucho más simple que las otras soluciones.

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

Tengo lo mismo:

ALTER TABLE DROP COLUMN falló porque uno o más objetos acceden a esta columna mensaje de .

Mi columna tenía un índice que primero debía eliminarse. El uso de sys.indexes hizo el truco:

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
Ewald Stieger
fuente
0

He actualizado el script un poco a mi versión del servidor SQL

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;
Łukasz Dawid Wątor
fuente
0

No siempre es solo una restricción predeterminada que evita que se caiga una columna y, a veces, los índices también pueden impedir que se caiga la restricción. Entonces escribí un procedimiento que elimina cualquier índice o restricción en una columna y la columna en sí misma al final.

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 ([email protected] 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 = ''
    ;
DataMatthis
fuente