2012-03-06 37 views
5

que tienen la siguiente estructura de la tabla para una tabla de alquiler:¿Cómo puedo evitar superposiciones de fechas en SQL?

hireId int primary key 
carId int not null foreign key 
onHireDate datetime not null 
offHireDate datetime not null 

que estoy tratando de programar un sistema multi-usuario que no permite periodo onhire y offhire para que los coches se superponen. Necesito poder agregar empleados en un orden no secuencial. También es necesario permitir la edición de los empleados.

¿Alguna forma de restringir las tablas o usar activadores, etc. para evitar superposiciones? Estoy usando marco de la entidad así que desea insertar en la tabla como normal y luego si no arrojar alguna excepción capturable etc.

+3

la única solución orientada a la base de datos que puedo pensar es la configuración de un disparador 'INSTEAD OF', un procedimiento que hace la comprobación. ¡pero lo evitaría y verificaría la superposición del código! – vulkanino

+0

Desde el punto de vista del marco de entidad no creo que pueda verificar fácilmente en el código. Puedo verificar en un punto, pero existe la posibilidad de que dos personas agreguen/editen eventos al mismo tiempo – GraemeMiller

+0

¡Pensé que el marco de entidades haría que el acceso a los datos sea más fácil, no más difícil! Es un cheque simple después de todo. La concurrencia es un problema, que puede resolver con bloqueo, si la arquitectura le permite (web/sin estado?) – vulkanino

Respuesta

3

Considere la siguiente consulta:

SELECT * 
FROM Hire AS H1, Hire AS H2 
WHERE H1.carId = H2.carId 
AND H1.hireId < H2.hireId 
AND 
    CASE 
    WHEN H1.onHireDate > H2.onHireDate THEN H1.onHireDate 
    ELSE H2.onHireDate END 
    < 
    CASE 
    WHEN H1.offHireDate > H2.offHireDate THEN H2.offHireDate 
    ELSE H1.offHireDate END 

Si todas las filas que cumplen regla comercial, entonces esta consulta será el conjunto vacío (suponiendo closed-open representation of periods, es decir, donde la fecha de finalización es el primer gránulo de tiempo que no se considera dentro del período).

Debido SQL Server does not support subqueries within CHECK constraints, poner la misma lógica en un disparador (pero no un disparador INSTEAD OF, a menos que pueda proporcionar la lógica para resolver se solapa).


consulta Alternativa utilizando Fowler:

SELECT * 
    FROM Hire AS H1, Hire AS H2 
WHERE H1.carId = H2.carId 
     AND H1.hireId < H2.hireId 
     AND H1.onHireDate < H2.offHireDate 
     AND H2.onHireDate < H1.offHireDate; 
+0

Entonces, ¿no debería usar el disparador En lugar de desencadenar para lanzar una excepción si ocurren solapamientos? Puedo detectar una excepción y hacer que el usuario lo solucione. – GraemeMiller

+0

@GraemeMiller: Use un disparador 'AFTER'. – onedaywhen

+0

¿Entonces insertaría el elemento y el disparador posterior lo retrotraería y generaría un error en la superposición? – GraemeMiller

3
CREATE TRIGGER tri_check_date_overlap ON your_table 
INSTEAD OF INSERT 
AS 
BEGIN 
    IF @@ROWCOUNT = 0 
     RETURN 

    -- check for overlaps in table 'INSERTED' 
    IF EXISTS(
     SELECT hireId FROM your_table WHERE 
      (INSERTED.onHireDate BETWEEN onHireDate AND offHireDate) OR 
      (INSERTED.offHireDate BETWEEN onHireDate AND offHireDate) 
    ) 
     BEGIN 
      -- exception? or do nothing? 
     END 
    ELSE 
     BEGIN 
     END 
END 
GO 
+0

no lo probé, no sé si el cheque realmente funciona. – vulkanino

+0

No comprueba las superposiciones si se insertan dos filas superpuestas en una sola instrucción de inserción (donde ninguna fila se superpone a una fila existente en la tabla) –

+0

@Damien_The_Unbeliever ¿se puede corregir el SP? – vulkanino

0

Ahora estoy usando técnicas de diseño de dominio impulsada. Esto me evita preocuparse por los desencadenadores de bases de datos, etc. y mantiene toda la lógica dentro del código .Net. La respuesta está aquí para que las personas puedan ver una alternativa.

Trato al principal de la colección de períodos como aggregate root y la raíz tiene una marca de tiempo. La raíz agregada es un límite de consistencia para transacciones, distribuciones y simultaneidad (ver Evans - what I've learned since the blue book). Esto se usa para verificar la última vez que se confirmó cualquier cambio en la base de datos.

Luego tengo métodos para agregar los períodos de alquiler al padre. Probé la superposición al agregar el movimiento a la raíz agregada. p.ej. AddHire(start,end) - validará que esto no crea superposición en el objeto de dominio en memoria.

AS no hay superposición Guardo los cambios (a través de mi repositorio), y compruebo que la marca de tiempo de la base de datos sigue siendo la misma que al comienzo del proceso. Suponiendo que la marca de tiempo es la misma que cuando recuperé la entidad, los cambios se mantienen y la base de datos actualiza la marca de tiempo.

Si alguien más intenta guardar los cambios cuando se está trabajando en la raíz del agregado, entonces confirmaré primero o lo harán. Si confirmo primero, las marcas de tiempo no coincidirán y la verificación de superposición se volverá a ejecutar para asegurarse de que no hayan creado una superposición en el tiempo intermedio.

Cuestiones relacionadas