Як скинути стовпчик із обмеженням?


138

Як скинути стовпчик, який має обмеження за замовчуванням у SQL Server 2008?

Мій запит:

alter table tbloffers
drop column checkin

Я отримую нижче помилки

ALTER TABLE DROP COLUMN не вдалося здійснити реєстрацію, оскільки один або більше об'єктів отримують доступ до цього стовпця.

Чи може хтось виправити мій запит, щоб скинути стовпець із обмеженнями?


Можуть бути деякі посилання на цю таблицю з інших таблиць, що викликає цю помилку.
Pankaj Upadhyay

Для нових бажаючих, які натрапляють на це, перегляньте мою відповідь нижче , якщо це працює для вас, це набагато простіше, ніж деякі інші рішення.
BrainSlugs83

Я перерахував моє запитання і відповідь тут
Akash Yellappa

Відповіді:


233

Спочатку слід скинути проблематичну 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

Дуже дякую - ти врятував мені багато часу. Я пов’язав питання, яке я задав тут
Акаш Єллаппа

130

Ось ще один спосіб скинути обмеження за замовчуванням з невідомим іменем без необхідності спочатку запускати окремий запит, щоб отримати ім'я обмеження:

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

27

Ви також можете опустити стовпчик та його обмеження в одному висловлюванні, а не окремо.

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 побачити граматику. Якщо ви чітко не називаєте обмеження, вам доведеться шукати ім'я, що генерується для нього, наприклад, відповідно до відповіді marc. Але виявивши це, ви все одно можете скинути обмеження та стовпчик одночасно.
Мартін Сміт

Хороший код, мені потрібно було одразу скинути обмеження мультиплексу, але не стовпець. Ваш альтер зробив свою справу. Дякую!!
htm11h

26

Знайдіть обмеження за замовчуванням за допомогою цього запиту тут:

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

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), так що YMMV, але, якщо це працює для вас, це waaaaay простіше, ніж інші рішення.

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

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 = ''
    ;
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.