2009-06-05 62 views
7

Qué tipo de trucos SQL utiliza para ingresar datos en dos tablas con una referencia circular entre ellas.Tratamiento de referencias circulares al ingresar datos en SQL

Employees 
    EmployeeID <PK> 
    DepartmentID <FK> NOT NULL 

Departments 
    DepartmentID <PK> 
    EmployeeID <FK> NOT NULL 

El empleado pertenece a un departamento, un departamento debe tener un gerente (jefe del departamento).

¿Debo desactivar las restricciones para que la inserción ocurra?

+0

Esto parece una extraña esquema para mí, ¿puede explicar por qué tiene las relaciones de esta manera? – Bob

+3

¿Está EmployeeId en la mesa de su departamento como un gerente de departamento o algo así? –

+0

En Oracle, el "truco" es definir la (s) clave (s) externa (s) DEFERRABLE (s). – spencer7593

Respuesta

5

Q: ¿Debo desactivar las restricciones para que la inserción ocurra?
A: En Oracle, no hay ni siquiera si las restricciones de claves foráneas son DEFERRABLE (ver ejemplo a continuación)

Para Oracle:

 
    SET CONSTRAINTS ALL DEFERRED; 
    INSERT INTO Departments values ('foo','dummy'); 
    INSERT INTO Employees values ('bar','foo'); 
    UPDATE Departments SET EmployeeID = 'bar' WHERE DepartmentID = 'foo'; 
    COMMIT; 

vamos a descomprimir que:

  • (autocommit debe estar desactivado)
  • aplazar la aplicación de la restricción de clave externa
  • insertar una fila a la tabla Departamento con un valor de "prueba" para la columna FK
  • insertar una fila de tabla de empleados con referencia FK al Departamento
  • sustituir el valor "ficticio" en el Departamento de FK con referencia verdadera
  • volver a habilitar la aplicación de las restricciones

NOTAS: la desactivación de una restricción de clave externa entra en vigor para todas las sesiones, difiriendo una restricción está en un nivel de transacción (como en el ejemplo), o en el nivel de sesión (ALTER SESSION SET CONSTRAINTS=DEFERRED;)

Oracle ha permitido que las restricciones de clave externa se definan como DEFERRABLE durante al menos una década. Defino todas las restricciones de clave externa (como una cuestión de rutina) para ser DEFERRABLE INICIALMENTE INMEDIATO. Eso mantiene el comportamiento predeterminado como todo el mundo espera, pero permite la manipulación sin necesidad de que se deshabiliten las claves externas.

ver AskTom: http://www.oracle.com/technology/oramag/oracle/03-nov/o63asktom.html

ver AskTom: http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:10954765239682

véase también: http://www.idevelopment.info/data/Oracle/DBA_tips/Database_Administration/DBA_12.shtml

[EDIT]

A: En Microsoft SQL Server, no se puede difiera las restricciones de clave externa como puede en Oracle. Desactivar y volver a habilitar la restricción de clave externa es un enfoque, pero tiemblo ante la perspectiva de 1) impacto en el rendimiento (la restricción de clave externa que se verifica para la tabla COMPLETA cuando la restricción se vuelve a habilitar), 2) manejar la excepción si (¿cuándo?) falla la reactivación de la restricción. Tenga en cuenta que la inhabilitación de la restricción afectará a todas las sesiones, por lo tanto, mientras la restricción esté desactivada, otras sesiones podrían potencialmente insertar y actualizar filas, lo que provocará que la restricción de la restricción falle.

Con SQL Server, un mejor enfoque es eliminar la restricción NOT NULL y permitir un NULL como marcador de posición temporal mientras se insertan/actualizan las filas.

Para SQL Server:

 
    -- (with NOT NULL constraint removed from Departments.EmployeeID) 
    insert into Departments values ('foo',NULL) 
    go 
    insert into Employees values ('bar','foo') 
    go 
    update Departments set EmployeeID = 'bar' where DepartmentID = 'foo' 
    go 

[/ EDIT]

+1

Acabo de enterarme de que Sybase también te permitirá hacerlo con el SET OPTION WAIT_FOR_COMMIT = 'ON'. Configurarlo en ON retrasará la verificación referencial hasta que llame a COMMIT. SET OPTION WAIT_FOR_COMMIT = 'ON'; insertar en Empleados ... insertar en Departamentos ... COMPROMISO; OPCIÓN DE CONFIGURACIÓN WAIT_FOR_COMMIT = 'OFF'; Ahora solo faltan MSSQL;) – Tom

+0

-1 "un mejor enfoque es eliminar la restricción NOT NULL": cómo se evita que el "marcador de posición temporal" permanezca indefinidamente. – onedaywhen

+0

@Tom: gracias por la nota útil acerca de habilitar la "comprobación de restricciones diferida" en Sybase. – spencer7593

0

Sí, en este caso deberá deshabilitar una clave externa.

1

Refactorice el esquema eliminando la referencia circular.
Eliminar una columna de ID de cualquiera de los esquemas de la tabla.

Departments.EmployeeID no parece pertenecer allí en mi opinión.

1

No puedo pensar en una forma no hackish de hacer esto. Creo que tendrá que eliminar la restricción o hacer algún tipo de tontos valores que se actualicen después de todas las inserciones.

Recomiendo la refacturación del esquema de base de datos. No puedo pensar en ninguna razón por la cual le gustaría que funcione de esta manera.

Tal vez algo así como, Empleado, Departamento de Empleados (Departamento de Empleados, Departamento) y Departamento sería una mejor manera de lograr el mismo objetivo.

0

Debe deshacerse de una u otra referencia permanentemente. Esta no es una estructura de diseño viable. ¿Cuál debe ser ingresado primero? ¿Departamento o empleado? A menos que todos sus departamentos sean un solo empleado, la estructura no tiene ningún sentido ya que cada empleado debería tener un departmento independiente.

+0

No sé por qué esa no es una estructura de diseño viable. El empleado pertenece a un departamento, un departamento debe tener un gerente (jefe de departamento). – Tom

+1

Ningún departamento no tiene que tener un gerente. Los puestos de jefe del departamento quedan vacantes. Pero no es viable porque nunca se puede tener una situación circular pk/fk y hacerlo funcionar con piratería para evitar las restricciones. La necesidad de hackear para evitar las limitaciones es su primera pista de que el diseño es malo. Creé una tabla de empleados que es la tabla principal, una tabla de organización que incluye la estructura organizativa y luego una tabla de asignaciones que tiene la estructura a la que está asignada la persona y la jerarquía (a la que se transfiere). Que tiene el FK para ambos. – HLGEM

13

Supongo que sus Departments.EmployeeID es un jefe de departamento. Lo que haría es hacer que esa columna sea nulable; entonces puedes crear el departamento primero, luego el empleado.

+1

No hay razón por la que no pueda tener una clave foránea en un campo que admite valores NULL. – Bill

+0

No, no puedes. Pero aún puede poner un índice para ayudar en las uniones ... –

+0

Caos: Sin duda, esa es una opción. Proyecto de ley: La idea era que cada departamento debe tener un jefe de departamento. – Tom

1

Se puede crear una fila en la tabla Departamento de 'asignados'

Para crear un nuevo departamento con un nuevo empleado, a continuación, le

  1. Crear el Empleado (EmployeeA) en el 'asignados' Departamento
  2. Crear nuevo departamento (departmentA) con el empleado EmployeeA
  3. actualización EmployeeA estar en departmentA

Esto no invalidaría su esquema actual, y podría configurar una tarea para que se ejecute regularmente para verificar que no haya miembros del departamento no asignado.

También sería necesario crear un empleado por defecto a ser el empleado de No asignado

EDIT:

La solución propuesta por el caos es mucho más simple, aunque

3

Este problema podría resolverse con limitaciones deferable .Dichas restricciones se verifican cuando se compromete toda la transacción, lo que le permite insertar empleados y departamentos en la misma transacción, refiriéndose entre sí. (Suponiendo que el modelo de datos tiene sentido)

+0

Suena como el enfoque descrito por Spencer7593. ¿Alguien sabe cómo eso en MSSQL? – Tom

1

Hay algunos buenos diseños que he usado. Todos implican la eliminación del EmployeeID "gerente" de la tabla Departamento y la eliminación de DepartmentID de la tabla Employee. He visto un par de respuestas que lo mencionan, pero aclararé cómo lo usamos:

Por lo general, termino con una tabla de enlace de relación de EmployeeDepartment - muchas a muchas, generalmente con indicadores como IsManager, IsPrimaryManager, IsAdmin, IsBackupManager, etc., que aclara la relación. Algunos pueden estar restringidos, por lo que solo se permite un administrador primario por departamento (aunque una persona puede ser un administrador principal de múltiples departamentos). Si no le gusta la tabla única, puede tener varias tablas: departamento de empleados, departamento de gerente, etc. pero podría tener situaciones en las que una persona es gerente pero no un empleado, etc.

También permitimos personas para ser miembros de múltiples departamentos.

Para un acceso simplificado, puede proporcionar vistas que realicen la unión de forma adecuada.

Cuestiones relacionadas