web-dev-qa-db-ja.com

制約付きのSQL Server 2005ドロップ列

「DEFAULT」制約のある列があります。その列を削除するスクリプトを作成したいと思います。

問題は、このエラーが返されることです。

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制約の名前はランダムに生成されているため、名前でドロップすることはできません。


更新
制約タイプは「DEFAULT」です。

あなたが提案した解決策を見ましたが、それらはすべて本当に「汚い」と思います...あなたは思いませんか? OracleとMySQLのどちらなのかわかりませんが、次のようなことが可能です:

DROP COLUMN xxx CASCADE CONSTRAINTS 

そして、関連するすべての制約を削除します...または、少なくともその列にマップされた制約を自動的に削除します(少なくともCHECK制約!)

MSSQLにはそのようなものはありませんか?

60
Julien N

このクエリは、指定されたテーブルのデフォルトの制約を見つけます。それはかなり、私は同意します:

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

[編集] Julien Nのコメントごとに更新

21
edosoft

デフォルトの制約とともに列を削除するスクリプトを次に示します。 MYTABLENAMEMYCOLUMNNAMEを適切に置き換えます。

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
64
Jeremy Stein

おそらくもう少し役立つかもしれません:

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変数のみを設定する必要があります。

16
jjroman

また、カスケードドロップを使用できないことは、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
pvolders
> 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 that:

残念ながら、列のデフォルト制約の名前は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'

私がそれを見つけるのは私だけですかcrazy私がドロップしようとしている列にのみ関係する制約を簡単に削除できないのですか?
名前を取得するために、3つの結合でリクエストを実行する必要があります...

6
Julien N

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
Steve

Information_schemaシステムビューを照会することにより、制約名を取得できます。

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

Jeremy Steinの答えに基づいて構築するために、このためのストアドプロシージャを作成し、既定の制約がある列またはない列の削除に使用できるようにセットアップしました。 sys.columnsを2回クエリするため、実際には効率的ではありませんが、機能します。

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
Stealth Rabbi

制約の名前を検索したり、MSSQLデザインビューを使用したりすることは必ずしもオプションではありません。現在、制約のある列を削除するスクリプトを作成したいと思います。名前が生成され、異なる環境Dev/Sys/Prod/etcでスクリプトを使用するため、名前の使用はオプションではありません。制約名は環境ごとに異なるため、名前を使用することはできません。おそらくシステムの表を調べる必要がありますが、より簡単なオプションが利用可能であることに同意します。

2
Jeroen

列を削除する前に明示的に制約を削除することは、「よりクリーンな」ソリューションだと思います。このようにして、気付いていない可能性のある制約を削除しません。それでもドロップに失敗する場合は、追加の制約が残っていることがわかります。私は自分のデータベースに何が起こっているかを正確に制御するのが好きです。

さらに、ドロップのスクリプティングにより、スクリプトが確実に保証され、結果が意図したとおりに正確に再現できることが期待されます。

1
DCNYAM

私はこれに出くわしました。 MSSQLデザインビューを使用して、制約のある列を削除できます。ドロップする列を右クリックして(制約の有無に関係なく)、問題なく削除できます。ハ..もうバカに見えた。

0
Bitmask

ランダムに生成されるとはどういう意味ですか? Management Studioの特定の列またはsys.tablesビューを使用して制約を検索し、名前を確認できます。

次に、列を削除する前に制約を削除するようにスクリプトを変更できます。これはどのような制約ですか?外部キー制約の場合、これを実行してもデータベース内のデータの整合性が損なわれないことを確認してください。

0
DCNYAM