¿Cómo elimino una restricción de clave externa solo si existe en el servidor sql?

235

Puedo descartar una tabla si existe usando el siguiente código pero no sé cómo hacer lo mismo con una restricción:

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

También agrego la restricción usando este código:

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

Respuestas:

321

La solución más simple se proporciona en la respuesta de Eric Isaacs . Sin embargo, encontrará restricciones en cualquier tabla. Si desea orientar una restricción de clave externa en una tabla específica, use esto:

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
fuente
1
es el si existe un poco que realmente estoy buscando ... lo siento. Actualizaré mi pregunta para que quede más claro.
solrevdev
2
Si está utilizando claves externas generadas por EF con puntos en el nombre, debe poner corchetes alrededor de los nombres como este [dbo]. [FK_dbo.MyTable_Etc]
David Sopko
En MSSQL 2017, parece que la columna ahora se llama en constraint_object_idlugar de soloobject_id
codenamezero
1
Esto no funciona OBJECT_ID ('[CONSTRAINT_NAME]', 'F') en una clave externa que sé que existe, y devolvió nulo.
Melbourne Developer
1
@MelbourneDeveloper Es posible que necesite el prefijo de esquema, si la clave externa existe en un esquema que no es dbo. Por ejemplo, IF (SELECT OBJECT_ID (N '[Schema]. [FK_Name]', N'F ')) NO ES NULL. Tuve problemas similares a usted (su solución también funcionó para mí), luego lo hice funcionar a través de este ejemplo. Estoy ejecutando SQL Server 2012.
iokevins
318

Esto es mucho más simple que la solución propuesta actual:

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

Si necesita eliminar otro tipo de restricción, estos son los códigos aplicables para pasar a la función OBJECT_ID () en la posición del segundo parámetro:

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

También puede usar OBJECT_ID sin el segundo parámetro.

Lista completa de tipos aquí :

Tipo de objeto:

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

Se aplica a: SQL Server 2012 a través de 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
fuente
1
Lista completa de tipos aquí (es decir, esto funciona para todo tipo de cosas , no solo claves).
ruffin
2
Tomó la libertad de agregar enlaces y listas de tipos.
Mitch Wheat
10
Parece que si la restricción no está en el esquema dbo, entonces también debe incluir el nombre del esquema. Por ejemplo: OBJECT_ID ('MySchema.FK_MyConstraint', 'F')
Giles Smith
1
Esta manera puede ser más simple, pero la otra es mejor para encontrar y eliminar explícitamente restricciones, incluso restricciones con el mismo nombre aplicado a diferentes tablas / esquemas / bases de datos.
CSS
1
Veo un problema aquí: nunca se asegura si el contenedor está en la tabla donde estamos soltando el contenedor.
sandeep rawat
15
IF (OBJECT_ID('DF_Constraint') IS NOT NULL)
BEGIN
    ALTER TABLE [dbo].[tableName]
    DROP CONSTRAINT DF_Constraint
END
DevDave
fuente
14

La respuesta de James funciona bien si conoce el nombre de la restricción real. Lo complicado es que en el legado y otros escenarios del mundo real, es posible que no sepas cómo se llama la restricción.

Si este es el caso, corre el riesgo de crear restricciones duplicadas, para evitar puede usar:

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 Azafrán
fuente
5
ALTER TABLE [dbo].[TableName]
    DROP CONSTRAINT FK_TableName_TableName2
Trigo Mitch
fuente
55
Quizás poner eso en un TRY..CATCHbloque.
cuando
"... si existe en el servidor sql? ..." - ¿cómo verifica que existe esa restricción?
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
fuente
Lo único que agregaría es incluir el nombre como filtro en la selección de sys.foreign_keys, ya que podría haber varias claves foráneas en la mesa
Koenyn
1

Creo que esto te ayudará ...

    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)

Eliminará la restricción de clave externa en función de una tabla y columna específicas.

Samir Savasani
fuente
0

Puede usar esas consultas para encontrar todos los FK de su tabla.

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
fuente
0

La respuesta aceptada a esta pregunta no parece funcionar para mí. Logré lo mismo con un método ligeramente diferente:

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
Melbourne Developer
fuente