Comment supprimer une contrainte de clé étrangère uniquement si elle existe sur le serveur SQL?

235

Je peux supprimer une table si elle existe en utilisant le code suivant mais je ne sais pas comment faire de même avec une contrainte:

IF EXISTS(SELECT 1 FROM sys.objects WHERE OBJECT_ID = OBJECT_ID(N'TableName') AND type = (N'U')) DROP TABLE TableName
go 

J'ajoute également la contrainte en utilisant ce code:

ALTER TABLE [dbo].[TableName] 
  WITH CHECK ADD CONSTRAINT [FK_TableName_TableName2] FOREIGN KEY([FK_Name])
    REFERENCES [dbo].[TableName2] ([ID])
go
solrevdev
la source

Réponses:

321

La solution la plus simple est fournie dans la réponse d' Eric Isaacs . Cependant, il trouvera des contraintes sur n'importe quelle table. Si vous souhaitez cibler une contrainte de clé étrangère sur une table spécifique, utilisez ceci:

IF EXISTS (SELECT * 
  FROM sys.foreign_keys 
   WHERE object_id = OBJECT_ID(N'dbo.FK_TableName_TableName2')
   AND parent_object_id = OBJECT_ID(N'dbo.TableName')
)
  ALTER TABLE [dbo.TableName] DROP CONSTRAINT [FK_TableName_TableName2]
James L
la source
1
son si existe peu je suis vraiment après .. désolé. je vais mettre à jour ma question donc c'est plus clair!
solrevdev le
2
Si vous utilisez des clés étrangères générées par EF avec des points dans le nom, vous devez mettre des crochets autour des noms comme celui-ci [dbo]. [FK_dbo.MyTable_Etc]
David Sopko
Dans MSSQL 2017, il semble que la colonne soit maintenant appelée constraint_object_idau lieu de simplementobject_id
codenamezero
1
Ça ne marche pas. OBJECT_ID ('[CONSTRAINT_NAME]', 'F') sur une clé étrangère que je connais existe, et il a renvoyé null.
Melbourne Developer
1
@MelbourneDeveloper Vous pourriez avoir besoin du préfixe de schéma, si la clé étrangère existe dans un schéma non-dbo. Par exemple, IF (SELECT OBJECT_ID (N '[Schema]. [FK_Name]', N'F ')) N'EST PAS NUL. J'ai eu des problèmes similaires à vous (votre solution a également fonctionné pour moi), puis je l'ai fait fonctionner via cet exemple.
J'exécute
318

C'est beaucoup plus simple que la solution actuelle proposée:

IF (OBJECT_ID('dbo.FK_ConstraintName', 'F') IS NOT NULL)
BEGIN
    ALTER TABLE dbo.TableName DROP CONSTRAINT FK_ConstraintName
END

Si vous devez supprimer un autre type de contrainte, voici les codes applicables à passer dans la fonction OBJECT_ID () dans la deuxième position du paramètre:

C = CHECK constraint
D = DEFAULT (constraint or stand-alone)
F = FOREIGN KEY constraint
PK = PRIMARY KEY constraint
UQ = UNIQUE constraint

Vous pouvez également utiliser OBJECT_ID sans le deuxième paramètre.

Liste complète des types ici :

Type d'objet:

AF = Aggregate function (CLR)
C = CHECK constraint
D = DEFAULT (constraint or stand-alone)
F = FOREIGN KEY constraint
FN = SQL scalar function
FS = Assembly (CLR) scalar-function
FT = Assembly (CLR) table-valued function
IF = SQL inline table-valued function
IT = Internal table
P = SQL Stored Procedure
PC = Assembly (CLR) stored-procedure
PG = Plan guide
PK = PRIMARY KEY constraint
R = Rule (old-style, stand-alone)
RF = Replication-filter-procedure
S = System base table
SN = Synonym
SO = Sequence object

S'applique à: SQL Server 2012 à SQL Server 2014.

SQ = Service queue
TA = Assembly (CLR) DML trigger
TF = SQL table-valued-function
TR = SQL DML trigger
TT = Table type
U = Table (user-defined)
UQ = UNIQUE constraint
V = View
X = Extended stored procedure
Eric Isaacs
la source
1
Liste complète des types ici (c'est-à-dire que cela fonctionne pour toutes sortes de choses , pas seulement pour les clés).
ruffin
2
A pris la liberté d'ajouter un lien et une liste de types.
Mitch Wheat
10
Il semble que si la contrainte ne se trouve pas dans le schéma dbo, vous devez également inclure le nom du schéma. Par exemple: OBJECT_ID ('MySchema.FK_MyConstraint', 'F')
Giles Smith
1
Cette méthode peut être plus simple, mais l'autre est préférable pour rechercher et supprimer explicitement des contraintes, même des contraintes portant le même nom et appliquées à différentes tables / schémas / bases de données.
CSS du
1
Je vois un problème ici, il ne s'assure jamais si le conteneur est sur la table où nous déposons le conteneur.
sandeep rawat
15
IF (OBJECT_ID('DF_Constraint') IS NOT NULL)
BEGIN
    ALTER TABLE [dbo].[tableName]
    DROP CONSTRAINT DF_Constraint
END
DevDave
la source
14

La réponse de James fonctionne très bien si vous connaissez le nom de la contrainte réelle. La chose délicate est que dans les scénarios hérités et autres scénarios réels, vous ne savez peut-être pas comment la contrainte est appelée.

Si tel est le cas, vous risquez de créer des contraintes en double, pour éviter, vous pouvez utiliser:

create function fnGetForeignKeyName
(
    @ParentTableName nvarchar(255), 
    @ParentColumnName nvarchar(255),
    @ReferencedTableName nvarchar(255),
    @ReferencedColumnName nvarchar(255)
)
returns nvarchar(255)
as
begin 
    declare @name nvarchar(255)

    select @name = fk.name  from sys.foreign_key_columns fc
    join sys.columns pc on pc.column_id = parent_column_id and parent_object_id = pc.object_id
    join sys.columns rc on rc.column_id = referenced_column_id and referenced_object_id = rc.object_id 
    join sys.objects po on po.object_id = pc.object_id
    join sys.objects ro on ro.object_id = rc.object_id 
    join sys.foreign_keys fk on fk.object_id = fc.constraint_object_id
    where 
        po.object_id = object_id(@ParentTableName) and 
        ro.object_id = object_id(@ReferencedTableName) and
        pc.name = @ParentColumnName and 
        rc.name = @ReferencedColumnName

    return @name
end

go

declare @name nvarchar(255)
declare @sql nvarchar(4000)
-- hunt for the constraint name on 'Badges.BadgeReasonTypeId' table refs the 'BadgeReasonTypes.Id'
select @name = dbo.fnGetForeignKeyName('dbo.Badges', 'BadgeReasonTypeId', 'dbo.BadgeReasonTypes', 'Id')
-- if we find it, the name will not be null
if @name is not null 
begin 
    set @sql = 'alter table Badges drop constraint ' + replace(@name,']', ']]')
    exec (@sql)
end
Sam Saffron
la source
5
ALTER TABLE [dbo].[TableName]
    DROP CONSTRAINT FK_TableName_TableName2
Blé Mitch
la source
5
Peut-être mettre cela dans un TRY..CATCHbloc.
onedaywhen
"... s'il existe dans le serveur sql? ..." - comment vérifiez-vous que la contrainte existe?
new2ios
3
Declare @FKeyRemoveQuery NVarchar(max)

IF EXISTS(SELECT 1 FROM sys.foreign_keys WHERE parent_object_id = OBJECT_ID(N'dbo.TableName'))

BEGIN
    SELECT @FKeyRemoveQuery='ALTER TABLE dbo.TableName DROP CONSTRAINT [' + LTRIM(RTRIM([name])) + ']'   
    FROM sys.foreign_keys
    WHERE parent_object_id = OBJECT_ID(N'dbo.TableName')

    EXECUTE Sp_executesql @FKeyRemoveQuery 

END
Rajalingam
la source
la seule chose supplémentaire que j'ajouterais est d'inclure le nom comme filtre dans la sélection de sys.foreign_keys car il pourrait y avoir plusieurs clés étrangères sur la table
Koenyn
1

Je pense que cela vous sera utile ...

    DECLARE @ConstraintName nvarchar(200)
SELECT 
    @ConstraintName = KCU.CONSTRAINT_NAME
FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS AS RC 
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS KCU
    ON KCU.CONSTRAINT_CATALOG = RC.CONSTRAINT_CATALOG  
    AND KCU.CONSTRAINT_SCHEMA = RC.CONSTRAINT_SCHEMA 
    AND KCU.CONSTRAINT_NAME = RC.CONSTRAINT_NAME
WHERE
    KCU.TABLE_NAME = 'TABLE_NAME' AND
    KCU.COLUMN_NAME = 'TABLE_COLUMN_NAME'
IF @ConstraintName IS NOT NULL EXEC('alter table TABLE_NAME drop  CONSTRAINT ' + @ConstraintName)

Il supprimera la contrainte de clé étrangère basée sur une table et une colonne spécifiques.

Samir Savasani
la source
0

Vous pouvez utiliser ces requêtes pour trouver tous les FK de votre table.

Declare @SchemaName VarChar(200) = 'Schema Name'
Declare @TableName VarChar(200) = 'Table name'

-- Find FK in This table.
SELECT 
    'IF  EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N''' + 
      '[' + OBJECT_SCHEMA_NAME(FK.parent_object_id) + '].[' + FK.name + ']' 
      + ''') AND parent_object_id = OBJECT_ID(N''' + 
      '[' + OBJECT_SCHEMA_NAME(FK.parent_object_id) + '].[' 
      + OBJECT_NAME(FK.parent_object_id) + ']' + ''')) ' +

    'ALTER TABLE ' +  OBJECT_SCHEMA_NAME(FK.parent_object_id) +
    '.[' + OBJECT_NAME(FK.parent_object_id) + 
    '] DROP CONSTRAINT ' + FK.name
    , S.name , O.name, OBJECT_NAME(FK.parent_object_id)
FROM sys.foreign_keys AS FK
INNER JOIN Sys.objects As O 
  ON (O.object_id = FK.parent_object_id )
INNER JOIN SYS.schemas AS S 
  ON (O.schema_id = S.schema_id)  
WHERE 
      O.name = @TableName
      And S.name = @SchemaName


-- Find the FKs in the tables in which this table is used
  SELECT 
    ' IF  EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N''' + 
      '[' + OBJECT_SCHEMA_NAME(FK.parent_object_id) + '].[' + FK.name + ']' 
      + ''') AND parent_object_id = OBJECT_ID(N''' + 
      '[' + OBJECT_SCHEMA_NAME(FK.parent_object_id) + '].[' 
      + OBJECT_NAME(FK.parent_object_id) + ']' + ''')) ' +

    ' ALTER TABLE ' +  OBJECT_SCHEMA_NAME(FK.parent_object_id) +
    '.[' + OBJECT_NAME(FK.parent_object_id) + 
    '] DROP CONSTRAINT ' + FK.name
    , S.name , O.name, OBJECT_NAME(FK.parent_object_id)
FROM sys.foreign_keys AS FK
INNER JOIN Sys.objects As O 
  ON (O.object_id = FK.referenced_object_id )
INNER JOIN SYS.schemas AS S 
  ON (O.schema_id = S.schema_id)  
WHERE 
      O.name = @TableName
      And S.name = @SchemaName 
Ardalan Shahgholi
la source
0

La réponse acceptée à cette question ne semble pas fonctionner pour moi. J'ai réalisé la même chose avec une méthode légèrement différente:

IF (select object_id from sys.foreign_keys where [name] = 'FK_TableName_TableName2') IS NOT NULL
BEGIN
    ALTER TABLE dbo.TableName DROP CONSTRAINT FK_TableName_TableName2
END
Développeur Melbourne
la source