Come posso eliminare un vincolo di chiave esterna solo se esiste nel server sql?


235

Posso eliminare una tabella se esiste utilizzando il seguente codice ma non so come fare lo stesso con un vincolo:

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

Aggiungo anche il vincolo usando questo codice:

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

Risposte:


321

La soluzione più semplice è fornita nella risposta di Eric Isaacs . Tuttavia, troverà vincoli su qualsiasi tabella. Se si desidera targetizzare un vincolo di chiave esterna su una tabella specifica, utilizzare questo:

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]

1
è il bit if esiste davvero dopo .. scusa. aggiornerò la mia domanda in modo che sia più chiaro!
solrevdev,

2
Se si utilizzano chiavi esterne generate da EF con punti nel nome, è necessario inserire parentesi attorno ai nomi come questo [dbo]. [FK_dbo.MyTable_Etc]
David Sopko

In MSSQL 2017, sembra che la colonna sia ora chiamata constraint_object_idanziché soloobject_id
codenamezero

1
Questo non funziona OBJECT_ID ('[CONSTRAINT_NAME]', 'F') su una chiave esterna che conosco esiste e ha restituito null.
Sviluppatore Melbourne

1
@MelbourneDeveloper Potrebbe essere necessario il prefisso dello schema, se la chiave esterna esiste in uno schema non dbo. Ad esempio, IF (SELECT OBJECT_ID (N '[Schema]. [FK_Name]', N'F ')) NON È NULL. Ho avuto problemi simili a te (la tua soluzione ha funzionato anche per me), quindi l'ho fatta funzionare tramite questo esempio. Sto eseguendo SQL Server 2012.
iokevins

318

Questo è molto più semplice dell'attuale soluzione proposta:

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

Se è necessario eliminare un altro tipo di vincolo, questi sono i codici applicabili da passare alla funzione OBJECT_ID () nella seconda posizione del parametro:

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

Puoi anche usare OBJECT_ID senza il secondo parametro.

Elenco completo dei tipi qui :

Tipo di oggetto:

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

Si applica a: da SQL Server 2012 a 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

1
Elenco completo dei tipi qui (vale a dire, questo funziona per tutti i tipi di cose , non solo per le chiavi).
Rufin,

2
Ha preso la libertà di aggiungere link ed elenco di tipi.
Mitch Wheat

10
Sembra che se il vincolo non è nello schema dbo, è necessario includere anche il nome dello schema. Ad esempio: OBJECT_ID ('MySchema.FK_MyConstraint', 'F')
Giles Smith

1
In questo modo può essere più semplice, ma l'altro è meglio per trovare e rimuovere esplicitamente i vincoli, anche i vincoli con lo stesso nome applicati a tabelle / schemi / database diversi.
CSS

1
Vedo un problema qui non si accerta mai se il contenitore si trova sul tavolo in cui si sta rilasciando contenere.
sandeep rawat,


15
IF (OBJECT_ID('DF_Constraint') IS NOT NULL)
BEGIN
    ALTER TABLE [dbo].[tableName]
    DROP CONSTRAINT DF_Constraint
END

14

La risposta di James funziona bene se si conosce il nome del vincolo effettivo. La cosa difficile è che nell'eredità e in altri scenari del mondo reale potresti non sapere come si chiama il vincolo.

In questo caso rischi di creare vincoli duplicati, per evitare puoi utilizzare:

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

5
ALTER TABLE [dbo].[TableName]
    DROP CONSTRAINT FK_TableName_TableName2

5
Forse mettilo in un TRY..CATCHblocco.
onedayquando il

"... se esiste nel server sql? ..." - come si verifica quel vincolo?
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

l'unica cosa in più che aggiungerei è includere il nome come filtro nella selezione da sys.foreign_keys in quanto potrebbero esserci più chiavi esterne sul tavolo
Koenyn,

1

Penso che questo ti sarà 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)

Eliminerà il vincolo di chiave esterna in base alla tabella e alla colonna specifiche.


0

È possibile utilizzare quelle query per trovare tutti gli FK per il proprio tavolo.

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 

0

La risposta accettata su questa domanda non sembra funzionare per me. Ho ottenuto la stessa cosa con un metodo leggermente diverso:

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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.