2010-03-30 18 views
56

Esta tabla se utiliza para almacenar sesiones (eventos):Comprobar superposición de los intervalos de fechas en MySQL

CREATE TABLE session (
    id int(11) NOT NULL AUTO_INCREMENT 
, start_date date 
, end_date date 
); 

INSERT INTO session 
    (start_date, end_date) 
VALUES 
    ("2010-01-01", "2010-01-10") 
, ("2010-01-20", "2010-01-30") 
, ("2010-02-01", "2010-02-15") 
; 

no queremos tener conflictos entre los rangos.
Digamos que tenemos que insertar una nueva sesión de 2010-01-05 a 2010-01-25.
Nos gustaría saber la (s) sesión (es) conflictiva (s).

Aquí es mi consulta:

SELECT * 
FROM session 
WHERE "2010-01-05" BETWEEN start_date AND end_date 
    OR "2010-01-25" BETWEEN start_date AND end_date 
    OR "2010-01-05" >= start_date AND "2010-01-25" <= end_date 
; 

aquí está el resultado:

+----+------------+------------+ 
| id | start_date | end_date | 
+----+------------+------------+ 
| 1 | 2010-01-01 | 2010-01-10 | 
| 2 | 2010-01-20 | 2010-01-30 | 
+----+------------+------------+ 

¿Hay una mejor manera de conseguir eso?


fiddle

+1

Su tercera condición es incorrecta. Se supone que es '" 2010-01-05 "<= start_date AND" 2010-01-25 "> = end_date'. Vea http://stackoverflow.com/a/28802972/632951 para la visualización. Su tercera condición actual nunca se evaluará, porque la primera (y segunda) condición ya la cubre. – Pacerier

Respuesta

110

tuve una consulta de este tipo con una aplicación de calendario Una vez escribí. Creo que he usado algo como esto:

... WHERE new_start < existing_end 
     AND new_end > existing_start; 

ACTUALIZACIÓN Esto definitivamente debe trabajar ((ns, ne, ES, EE) = (new_start, new_end, existing_start, existing_end)):

  1. ns - ne - es - ee: no se solapa y no se corresponde con (porque ne < en)
  2. ns - es - ne - EE: superposiciones y fósforos
  3. es - ns - ee - ne: superposiciones y coincide
  4. es - ee - ns - ne: no se superponen y no coincide (porque ns> ee)
  5. es - ns - ne - EE: superposiciones y partidos
  6. ns - es - ee - ne: superposiciones y partidos

Aquí es una fiddle

+0

@Glide: Creo que debería funcionar, la respuesta actualizada – soulmerge

+6

¡Funciona muy bien !, pero creo que @Pierre de LESPINAY está buscando rangos inclusivos en su consulta: WHERE new_start <= existing_end AND new_end> = existing_start; –

+11

@OsvaldoM. Si realmente lo fuera, se habría quejado hace 2 años ... – soulmerge

18
SELECT * FROM tbl WHERE 
existing_start BETWEEN $newStart AND $newEnd OR 
existing_end BETWEEN $newStart AND $newEnd OR 
$newStart BETWEEN existing_start AND existing_end 

if (!empty($result)) 
throw new Exception('We have overlapping') 

Estas 3 líneas de cláusulas SQL cubren los 4 casos de la superposición requerida.

+3

good job mate :) – Adrian

+3

Aunque aparentemente el OP no estaba buscando esta definición superpuesta, esta respuesta es la mejor solución para el problema descrito por el nombre de la pregunta. Estaba buscando esta superposición, que es la verdadera superposición. – Cec

+2

Fantástico, me salvó un gran dolor de cabeza. –

11

La respuesta de Lamy es buena, pero puedes optimizarla un poco más.

SELECT * FROM tbl WHERE 
existing_start BETWEEN $newSTart AND $newEnd OR 
$newStart BETWEEN existing_start AND existing_end 

Esto capturará los cuatro escenarios donde los rangos se superponen y excluye los dos donde no lo hacen.

+0

¿Existen otras soluciones además de esta y las otras dos anteriores? – Pacerier

3

Me he enfrentado al problema similar. Mi problema era dejar de reservar entre una variedad de fechas bloqueadas. Por ejemplo, la reserva está bloqueada para una propiedad entre el 2 de mayo y el 7 de mayo. Necesitaba encontrar cualquier tipo de fecha de superposición para detectar y detener la reserva. Mi solución es similar a LordJavac.

SELECT * FROM ib_master_blocked_dates WHERE venue_id=$venue_id AND 
(
    (mbd_from_date BETWEEN '$from_date' AND '$to_date') 
    OR 
    (mbd_to_date BETWEEN '$from_date' AND '$to_date') 
    OR 
    ('$from_date' BETWEEN mbd_from_date AND mbd_to_date) 
    OR  
    ('$to_date' BETWEEN mbd_from_date AND mbd_to_date)  
) 
*mbd=master_blocked_dates 

Avísame si no funciona.

1

Dados dos intervalos como (s1, e1) y (S2, e2) con s1 < e1 y s2 < e2
se puede calcular la superposición de la siguiente manera:

SELECT 
    s1, e1, s2, e2, 
    ABS(e1-s1) as len1, 
    ABS(e2-s2) as len2, 
    GREATEST(LEAST(e1, e2) - GREATEST(s1, s2), 0)>0 as overlaps, 
    GREATEST(LEAST(e1, e2) - GREATEST(s1, s2), 0) as overlap_length 
FROM test_intervals 

también funcionará si un intervalo es dentro del otro.

0

Recientemente he tenido problemas con el mismo tema y llegó a su fin con este sencillo paso (Esto puede no ser una buena aproximación o consumo de memoria) -

SELECT * FROM duty_register WHERE employee = '2' AND (
(
duty_start_date BETWEEN {$start_date} AND {$end_date} 
OR 
duty_end_date BETWEEN {$start_date} AND {$end_date} 
) 
OR 
(
{$start_date} BETWEEN duty_start_date AND duty_end_date 
OR 
{$end_date} BETWEEN duty_start_date AND duty_end_date) 
); 

Esto me ayudó a encontrar las entradas con fecha de superposición rangos.

Espero que esto ayude a alguien.

Cuestiones relacionadas