Столбец SQL Server 2005 с ограничениями

У меня есть столбец с ограничением "ПО УМОЛЧАНИЮ". Я хотел бы создать script, который удаляет этот столбец.

Проблема заключается в том, что она возвращает эту ошибку:

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 

И он устраняет все связанные ограничения... Или, по крайней мере, он автоматически удаляет ограничения, сопоставленные этому столбцу (по крайней мере, ограничения CHECK!)

В MSSQL ничего подобного нет?

Ответ 1

Этот запрос находит ограничения по умолчанию для данной таблицы. Это довольно, я согласен:

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] Обновлено за сообщение Julien N

Ответ 2

Вот script, который удалит столбец вместе со своим значением по умолчанию. Замените MYTABLENAME и MYCOLUMNNAME соответствующим образом.

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

Ответ 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'

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

Ответ 5

Я также считаю, что недостатком на SQL-сервере не является доступный каскадный доступ. Я проработал вокруг него, запросив системные таблицы так же, как и другие люди, описанные здесь:

  • INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE перечисляет только внешние ключи, первичные ключи и уникальные ограничения.
  • Единственный способ поиска ограничений по умолчанию - искать их в sys.default_constraints.
  • то, что еще не упоминалось здесь, заключается в том, что индексы также вызывают отказ столбца, поэтому вам также нужно отбросить все индексы, которые используют ваш столбец, прежде чем вы сможете продолжить удаление столбца.

Результат script не очень хорош, но я поместил его в хранимую процедуру, чтобы иметь возможность повторно использовать его:

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

Вы можете получить имена ограничений, запросив системные представления Information_schema.

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

Ответ 7

Ответ от 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

Ответ 8

Чтобы ответить на вопрос Джереми Штейна, я создал для этого хранимую процедуру и настроил ее так, чтобы ее можно было использовать для удаления столбца, который имеет или не имеет ограничений по умолчанию. Это не реально эффективно, так как он дважды запрашивает sys.column, но он работает.

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

Я считаю, что явное сокращение ограничений до сброса столбца является "более чистым" решением. Таким образом, вы не отказываетесь от ограничений, о которых вы, возможно, не знаете. Если падение все еще не удается, вы знаете, что есть дополнительные ограничения. Мне нравится контролировать то, что происходит с моей базой данных.

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

Ответ 10

Поиск имени contraint или использование MSSQL-дизайна не всегда является опцией. В настоящее время я хочу сделать script для удаления столбца с указанием на него. Использование имени не является параметром, поскольку имя сгенерировано, и я хочу использовать script в разных средах Dev/Sys/Prod/etc. Использование имени тогда невозможно, потому что имена ограничений будут отличаться для каждой среды. Мне, вероятно, придется заглянуть в системные таблички, но я согласен, что должен быть более простой вариант.

Ответ 11

Что значит "случайно"? Вы можете найти ограничения на конкретный столбец в студии управления или через представление sys.tables и найти имена (имена).

Затем вы можете изменить свой script, чтобы удалить ограничения перед тем, как удалить столбец. Что это за ограничение? Если это ограничение внешнего ключа, убедитесь, что это не повредит целостности данных в вашей базе данных.

Ответ 12

Я просто столкнулся с этим. Вы можете удалить столбец с ограничениями, используя представление MSSQL. Щелкните правой кнопкой мыши на столбце, который вы хотите удалить (с ограничениями или без ограничений), и вы можете удалить его без каких-либо проблем. Ха.. Я уже выглядел глупо.

Ответ 13

Просто создайте скрипты для таблицы. Там вы можете найти имя всех ограничений.