「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にはそのようなものはありませんか?
このクエリは、指定されたテーブルのデフォルトの制約を見つけます。それはかなり、私は同意します:
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のコメントごとに更新
デフォルトの制約とともに列を削除するスクリプトを次に示します。 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
おそらくもう少し役立つかもしれません:
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変数のみを設定する必要があります。
また、カスケードドロップを使用できないことは、SQLサーバーの欠点だと思います。ここで説明した他の人と同じ方法でシステムテーブルをクエリすることで、この問題を回避しました。
結果のスクリプトはきれいではありませんが、ストアドプロシージャに入れて再利用できるようにします。
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
> 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つの結合でリクエストを実行する必要があります...
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
Information_schemaシステムビューを照会することにより、制約名を取得できます。
select CONSTRAINT_NAME from INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE WHERE TABLE_NAME = '<tablename>' AND COLUMN_NAME = 'IsClosed'
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
制約の名前を検索したり、MSSQLデザインビューを使用したりすることは必ずしもオプションではありません。現在、制約のある列を削除するスクリプトを作成したいと思います。名前が生成され、異なる環境Dev/Sys/Prod/etcでスクリプトを使用するため、名前の使用はオプションではありません。制約名は環境ごとに異なるため、名前を使用することはできません。おそらくシステムの表を調べる必要がありますが、より簡単なオプションが利用可能であることに同意します。
列を削除する前に明示的に制約を削除することは、「よりクリーンな」ソリューションだと思います。このようにして、気付いていない可能性のある制約を削除しません。それでもドロップに失敗する場合は、追加の制約が残っていることがわかります。私は自分のデータベースに何が起こっているかを正確に制御するのが好きです。
さらに、ドロップのスクリプティングにより、スクリプトが確実に保証され、結果が意図したとおりに正確に再現できることが期待されます。
私はこれに出くわしました。 MSSQLデザインビューを使用して、制約のある列を削除できます。ドロップする列を右クリックして(制約の有無に関係なく)、問題なく削除できます。ハ..もうバカに見えた。
ランダムに生成されるとはどういう意味ですか? Management Studioの特定の列またはsys.tablesビューを使用して制約を検索し、名前を確認できます。
次に、列を削除する前に制約を削除するようにスクリプトを変更できます。これはどのような制約ですか?外部キー制約の場合、これを実行してもデータベース内のデータの整合性が損なわれないことを確認してください。