Как удалить столбец с ограничением?

144

Как удалить столбец, который имеет ограничение по умолчанию в SQL Server 2008?

Мой запрос

alter table tbloffers
drop column checkin

Я получаю ошибку ниже

ALTER TABLE DROP COLUMN не удалось выполнить проверку, поскольку один или несколько объектов обращаются к этому столбцу.

Может ли кто-нибудь исправить мой запрос, чтобы удалить столбец с ограничением?

Робин Майкл Путураи
источник
могут быть ссылки на эту таблицу из других таблиц, которые вызывают эту ошибку.
Панкадж Упадхьяй
Для новичков, которые сталкиваются с этим, ознакомьтесь с моим ответом ниже , если он работает для вас, это намного проще, чем некоторые другие решения.
BrainSlugs83
Я перечислил мой вопрос и ответ здесь
Akash Yellappa

Ответы:

242

Сначала вы должны отбросить проблемный DEFAULT constraint, после этого вы можете удалить столбец

alter table tbloffers drop constraint [ConstraintName]
go

alter table tbloffers drop column checkin

Но ошибка может появиться и по другим причинам - например, из-за пользовательской функции или представления с SCHEMABINDINGустановленной для них опцией.

UPD: Полностью автоматизированный скрипт сброса ограничений:

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
Олег Док
источник
1
Спасибо за автоматизированный скрипт. Работает как шарм!
kanadianDri3
Большое вам спасибо - вы сэкономили мне много времени. Я связал здесь вопрос, который задал,
Акаш Йеллаппа
139

Вот еще один способ отбросить ограничение по умолчанию с неизвестным именем без предварительного запуска отдельного запроса для получения имени ограничения:

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)
Крис Хэлкроу
источник
Отличный ответ, спасибо. Тем не менее, я также поддержал приведенный выше ответ просто из-за этой дрожащей старой привычки выбирать и проверять сначала, прежде чем принять решение.
noogrub
7
Действительно отличный ответ. Я сделал из него хранимую процедуру для удобства / использования в будущем: pastebin.com/2CeXZDh2
Digs
Отличный ответ, но все еще отсутствует подход, когда к столбцу привязано более одного ограничения. Некоторые сохраненные процедуры, похожие на сообщение @Digs с включенным циклом, могут быть 5-звездочными ответами
YeinCM-Qva
28

Вы также можете удалить столбец и его ограничения в одном выражении, а не по отдельности.

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
1
@Joey - не существует синтаксиса для удаления ограничений без знания имени. Это обязательный аргумент для DROP CONSTRAINT просмотра грамматики. Если вы не укажете ограничения явно, вам придется искать сгенерированное для него имя SQL Server, например, в соответствии с ответом Марка. Но, обнаружив это, вы все равно можете удалить ограничение и столбец одновременно.
Мартин Смит
Хороший код, мне нужно было сразу удалить несколько ограничений, но не столбец. Ваш альтер сделал свое дело. Благодарность!!
htm11h
27

Найдите ограничение по умолчанию с помощью этого запроса здесь:

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
marc_s
источник
2
Необязательно делать это последовательно. Вы можете делать и то и другое одновременно.
Мартин Смит
1
@MartinSmith: Хорошо, отлично - спасибо, что поделились! Я не знал о такой возможности - каждый день узнаешь что-то новое! :-)
marc_s
Может ли кто-нибудь привести пример того, как объединить эти два утверждения. Мне нужно что-то вроде: ALTER TABLE table DROP CONSTRAINT DF_XY DROP COLUMN XYК сожалению, синтаксис этого утверждения неверен
My-Name-Is
1
@ My-Name-Is: если вы посмотрите ответ Мартина, вам нужно поставить запятую между двумя DROPкомандами, чтобы это сработало
marc_s
3

Следующее работало для меня против бэкэнда SQL Azure (с использованием SQL Server Management Studio), поэтому YMMV, но, если это работает для вас, это вааааай проще, чем другие решения.

ALTER TABLE MyTable
    DROP CONSTRAINT FK_MyColumn
    CONSTRAINT DK_MyColumn
    -- etc...
    COLUMN MyColumn
GO
BrainSlugs83
источник
1

У меня то же самое:

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
Эвальд Стигер
источник
0

Я немного обновил скрипт для своей версии 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;

Лукаш Давид Вэтор
источник
0

Это не всегда просто ограничение по умолчанию, которое предотвращает удаление столбца, и иногда индексы также могут заблокировать вам удаление ограничения. Поэтому я написал процедуру, которая удаляет любой индекс или ограничение для столбца и самого столбца в конце.

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 = ''
    ;
DataMatthis
источник