J'ai une colonne avec une contrainte "DEFAULT". Je voudrais créer un script qui supprime cette colonne.
Le problème est qu'il renvoie cette erreur:
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.
Je n'ai pas pu trouver un moyen facile de supprimer une colonne et toutes ses contraintes associées (seulement trouvé de gros scripts qui regardent dans la table système ... il DOIT (!!) être une "belle" façon de le faire.)
Et comme le nom de la contrainte DEFAULT a été généré de manière aléatoire, je ne peux pas le supprimer par son nom.
Mise à jour:
Le type de contrainte est "DEFAUT".
J'ai vu les solutions que vous avez toutes proposées mais je les trouve toutes vraiment "sales" ... Vous ne pensez pas? Je ne sais pas si c'est avec Oracle ou MySQL mais il est possible de faire quelque chose comme:
DROP COLUMN xxx CASCADE CONSTRAINTS
Et il supprime toutes les contraintes associées ... Ou au moins il supprime automatiquement les contraintes mappées à cette colonne (au moins CHECK contraintes!)
N'y a-t-il rien de tel dans MSSQL?
Cette requête trouve les contraintes par défaut pour une table donnée. Ce n'est pas joli, je suis d'accord:
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] Mis à jour selon le commentaire de Julien N
Voici un script qui supprimera la colonne ainsi que sa contrainte par défaut. Remplacez MYTABLENAME
et MYCOLUMNNAME
de manière appropriée.
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
Cela pourrait peut-être aider un peu plus:
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
Il suffit de définir les variables @tablename & @colname pour supprimer la colonne.
Je pense également que c'est une lacune dans SQL Server de ne pas avoir de goutte en cascade disponible. J'ai travaillé autour de lui en interrogeant les tables système de la même manière que les autres personnes décrites ici:
Le script résultant n'est pas joli, mais je l'ai mis dans une procédure stockée pour pouvoir le réutiliser:
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'
Ce n'est pas la bonne solution, comme cela est expliqué ici: http://msdn.Microsoft.com/en-us/library/aa175912.aspx que:
Malheureusement, le nom de la contrainte par défaut de la colonne n'est pas conservé dans la vue ANSI COLUMNS, vous devez donc revenir aux tables système pour trouver le nom
Le seul moyen que j'ai trouvé pour obtenir le nom de la contrainte DEFAULT est cette requête:
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'
Suis-je le seul à le trouver fou pour ne pas pouvoir supprimer facilement une contrainte qui ne concerne que les colonnes que j'essaye de supprimer?
J'ai besoin d'exécuter une demande avec 3 jointures juste pour obtenir le nom ...
La réponse des pvolders était exactement ce dont j'avais besoin mais il manquait des statistiques qui causaient et des erreurs. Il s'agit du même code, moins la création d'une procédure stockée, plus l'énumération des statistiques et leur suppression. C'est le meilleur que j'ai pu trouver, donc s'il existe un meilleur moyen de déterminer quelles statistiques doivent être supprimées, veuillez ajouter.
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
Vous pouvez obtenir les noms des contraintes en interrogeant les vues système information_schema.
select CONSTRAINT_NAME from INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE WHERE TABLE_NAME = '<tablename>' AND COLUMN_NAME = 'IsClosed'
Juste pour construire sur la réponse de Jeremy Stein, j'ai créé une procédure stockée pour cela, et je l'ai configurée pour qu'elle puisse être utilisée pour supprimer une colonne qui a ou n'a pas de contraintes par défaut. Ce n'est pas vraiment efficace car il interroge sys.columns deux fois, mais cela fonctionne.
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
Rechercher le nom de la contraint ou utiliser la vue de conception MSSQL n'est pas toujours une option. Je veux actuellement créer un script pour supprimer une colonne avec une contraint dessus. Utiliser le nom n'est pas une option car le nom est généré et je veux utiliser le script dans différents environnements Dev/Sys/Prod/etc. L'utilisation du nom n'est alors pas possible car les noms de contraint diffèrent selon l'environnement. Je devrai probablement examiner le tableau des systèmes, mais je suis d'accord qu'il devrait y avoir une option plus simple disponible.
Je crois que supprimer explicitement les contraintes avant de supprimer la colonne est une solution "plus propre". De cette façon, vous ne supprimez pas les contraintes dont vous n'êtes peut-être pas conscient. Si la suppression échoue toujours, vous savez qu'il reste des contraintes supplémentaires. J'aime contrôler exactement ce qui arrive à ma base de données.
De plus, l'écriture des scripts garantit explicitement le script et, espérons-le, les résultats peuvent être reproduits exactement comme vous le souhaitez.
Je suis juste tombé sur ça. Vous pouvez supprimer la colonne avec des contraintes à l'aide de la vue de conception MSSQL. Faites un clic droit sur la colonne que vous souhaitez supprimer (avec ou sans contraintes) et vous pouvez la supprimer sans aucun problème. Ha .. J'avais déjà l'air stupide.
Que voulez-vous dire généré de façon aléatoire? Vous pouvez rechercher les contraintes sur la colonne spécifique dans le studio de gestion ou via la vue sys.tables et trouver le ou les noms.
Ensuite, vous pouvez modifier votre script pour supprimer les contraintes avant de supprimer la colonne. De quel type de contrainte s'agit-il? S'il s'agit d'une contrainte de clé étrangère, assurez-vous que cela ne nuira pas à l'intégrité des données dans votre base de données.