SQL Server 2005 удалить столбец с ограничениями - PullRequest
60 голосов
/ 24 ноября 2008

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

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

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 ничего подобного?

Ответы [ 13 ]

64 голосов
/ 23 июля 2009

Вот скрипт, который удалит столбец вместе с ограничением по умолчанию. Замените 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
21 голосов
/ 12 декабря 2008

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

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

[РЕДАКТИРОВАТЬ] Обновлено за комментарий Жюльена Н

16 голосов
/ 24 июля 2010

Возможно, это могло бы помочь немного больше:

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 для удаления столбца.

6 голосов
/ 07 ноября 2010

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

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

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

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 голосов
/ 25 ноября 2008
> 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'

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

4 голосов
/ 26 июля 2012

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

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
4 голосов
/ 31 августа 2011

Ответ от 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
4 голосов
/ 24 ноября 2008

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

select CONSTRAINT_NAME from INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE WHERE TABLE_NAME = '<tablename>' AND COLUMN_NAME = 'IsClosed'
2 голосов
/ 20 июля 2011

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

1 голос
/ 24 ноября 2008

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...