2011-06-10 26 views
14

No se puede eliminar una fila si alguna fila hace referencia a la fila para eliminar a través de un FK.Servidor SQL: cómo saber si una fila hace referencia a la fila para eliminar

¿Es posible saber si una fila está haciendo referencia a la fila que se va a eliminar antes de ejecutar una instrucción DELETE?

+0

posible duplicado de [Ayuda en el gatillo de SQL Server] (http://stackoverflow.com/questions/6244077/help-on-sql -server-trigger) – gbn

+0

Mi objetivo no es provocar una eliminación en cascada, sino saber de antemano si el registro es eliminable. Si no es eliminable, se notificará al usuario que no se puede eliminar. – Yeonho

+2

Sé que ya ha marcado una respuesta, pero si se trata de un sistema multiusuario, la mejor respuesta puede ser simplemente intentar eliminar y hacer frente a un error; de lo contrario, se pueden realizar todo tipo de condiciones de carrera aquí, salvo envuelve todo en una transacción con alto aislamiento. –

Respuesta

18

Este script mostrará todas las tablas que tienen filas que hacen referencia a la fila que está intentando eliminar:

declare @RowId int = 1 
declare @TableName sysname = 'ParentTable' 

declare @Command varchar(max) 

select @Command = isnull(@Command + ' union all ', '') + 'select ''' + object_name(parent_object_id) + 
    ''' where exists(select * from ' + object_name(parent_object_id) + ' where ' + col.name+ ' = ' + cast(@RowId as varchar) + ')' 
from sys.foreign_key_columns fkc 
    join sys.columns col on 
     fkc.parent_object_id = col.object_id and fkc.parent_column_id = col.column_id 
where object_name(referenced_object_id) = @TableName 

execute (@Command) 

asunción de que la clave externa no es compuesto.

3

Opción 1 (Detección):

Realiza una Select Statement para determinar si alguno registros hacen referencia al ser eliminados por el Record-a-- y, si lo desea , eliminar manualmente los registros que haz referencia. Esto también se puede lograr utilizando un disparador, aunque recomiendo los desencadenantes, ya que tienden a sorprender a las personas (y usted) en el futuro.

Opción 2 (automatización):

Usted puede mirar en Cascading Deletes cuales, si se ha configurado correctamente, hará que todos los registros que hacen referencia al registro-a-ser-suprimido a borrar también.

Cuándo utilizar eliminaciones en cascada (parafraseado de texto escrito por Joel Coehoorn)

  • Eliminar en cascada puede tener sentido cuando la semántica de la relación pueden implicar una "forma parte de la" descripción. Ejemplo: Orden web, líneas de pedido web
  • No debe utilizar Cascade Delete si conserva el historial o utiliza una eliminación suave donde solo establece una columna de bit eliminado
  • La conexión en cascada puede ocasionar problemas si configura su claves extranjeras mal.
  • No es aconsejable utilizar en cascada antes de comprenderlo a fondo. Sin embargo, es una característica útil y, por lo tanto, vale la pena tomarse el tiempo para comprender.

Aquí hay una gran discusión en Cascading Deletes en stackoverflow.

+0

cuando intenta eliminar un registro con un FK, SQL Server arroja una excepción. ¿SQL Server realiza internamente una instrucción SELECT para saber si hay algún registro que la haga referencia? – Yeonho

+2

SQL realiza un seguimiento de las relaciones y restricciones en una variedad de formas, algunas de esas formas son similares a las instrucciones de selección. Sin embargo, pienso que es más como una recolección de basura. "Nada hace referencia a este pedazo de memoria, por lo tanto, puedo eliminarlo" –

0

nadie lo ha mencionado, pero sólo para el registro que utilizar una gran cantidad del procedimiento

sp_helpconstraint 'dbo.mytable' 

con el fin de encontrar todas las limitaciones relacionadas con dbo.MyTable y que hacen referencia a tablas dbo.MyTable. Lo encuentro muy útil y útil.

0

He mejorado la solución de Alex Aza.

Uso softdelete, por lo que es necesario agregar una columna "eliminar" en la condición "donde". Y más hice una función en TSQL que descubre cuando la tabla representa el objeto heredado en NHibernate, y PK es el FK de la tabla padre.

siga:

declare @RowId int = 4 
declare @TableName sysname = 'TABLE' 

declare @Command varchar(max) 

select @Command = isnull(@Command + ' union all ', '') + 'select ''' + object_name(parent_object_id) + 
    ''' where exists(select * from ' + object_name(parent_object_id) + 
    CASE 
     WHEN EXISTS(select object_name(object_id) from sys.columns col where name = 'deleted' and object_id = parent_object_id) 
      THEN ' where ' + col.name+ ' = ' + cast(@RowId as varchar) +' and deleted = 0 ' 
     when dbo.ParentIdFromTable(object_name(parent_object_id)) <> '' 
      then ' inner join ' + dbo.ParentIdFromTable(object_name(parent_object_id)) + ' on id = ' + dbo.PrimaryKey(object_name(parent_object_id)) 
       +' where ' + col.name+ ' = ' + cast(@RowId as varchar) +' and deleted = 0 ' 
     else 
      ' where ' + col.name+ ' = ' + cast(@RowId as varchar) 
     END 
    + ')' 
from sys.foreign_key_columns fkc 
    join sys.columns col on 
     fkc.parent_object_id = col.object_id and fkc.parent_column_id = col.column_id 
where object_name(referenced_object_id) = @TableName 

PRINT @Command 
execute (@Command) 

depedencies Funciones:

CREATE FUNCTION dbo.ParentIdFromTable(@Table varchar(255)) 
RETURNS varchar(255) 
AS 
BEGIN 
    declare @tableParent varchar(255) = '' 

    if exists(select pk.TABLE_NAME, pk.COLUMN_NAME, col.name, object_name(referenced_object_id) Referenced, object_name(parent_object_id) as Parent 
     from sys.columns col 
      inner join sys.foreign_key_columns fkc on 
       fkc.parent_object_id = col.object_id and fkc.parent_column_id = col.column_id 
      inner join INFORMATION_SCHEMA.KEY_COLUMN_USAGE pk on 
       pk.TABLE_NAME = object_name(object_id) and pk.COLUMN_NAME = col.name 
     WHERE OBJECTPROPERTY(OBJECT_ID(constraint_name), 'IsPrimaryKey') = 1 
     AND table_name = @table) 
    begin 

     while exists(select * 
      from sys.columns col 
      inner join sys.foreign_key_columns fkc on 
       fkc.parent_object_id = col.object_id and fkc.parent_column_id = col.column_id 
      inner join INFORMATION_SCHEMA.KEY_COLUMN_USAGE pk on 
       pk.TABLE_NAME = object_name(object_id) and pk.COLUMN_NAME = col.name 
      WHERE OBJECTPROPERTY(OBJECT_ID(constraint_name), 'IsPrimaryKey') = 1 AND table_name = @table) 
     begin 
      -- Descobrir o parent, column 
      select @tableParent = object_name(referenced_object_id) 
      from sys.columns col 
      inner join sys.foreign_key_columns fkc on 
       fkc.parent_object_id = col.object_id and fkc.parent_column_id = col.column_id 
      inner join INFORMATION_SCHEMA.KEY_COLUMN_USAGE pk on 
       pk.TABLE_NAME = object_name(object_id) and pk.COLUMN_NAME = col.name 
      WHERE OBJECTPROPERTY(OBJECT_ID(constraint_name), 'IsPrimaryKey') = 1 
      AND table_name = @table 

      --print @tableParent 
      set @table = @tableParent 
     end 
    end 

    return @tableParent; 
END; 
GO 


CREATE FUNCTION dbo.PrimaryKey(@Table varchar(255)) 
RETURNS varchar(255) 
AS 
BEGIN 
    declare @columnName varchar(255) = '' 
    -- Descobrir o parent, column 
    select @columnName = COLUMN_NAME 
    from INFORMATION_SCHEMA.KEY_COLUMN_USAGE 
    WHERE OBJECTPROPERTY(OBJECT_ID(constraint_name), 'IsPrimaryKey') = 1 
    AND table_name = @Table 

    return @columnName 
end; 
Cuestiones relacionadas