2010-03-30 26 views
56

Bảng này được sử dụng để lưu trữ phiên (sự kiện):Kiểm tra chồng chéo của các phạm vi ngày trong 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") 
; 

Chúng tôi không muốn có xung đột giữa dãy.
Giả sử chúng ta cần chèn một phiên mới từ 2010-01-05 đến 2010-01-25.
Chúng tôi muốn biết (các) phiên xung đột.

Đây là câu hỏi của tôi:

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 
; 

Dưới đây là kết quả:

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

Có cách nào tốt hơn để có được điều đó?


fiddle

+1

Điều kiện thứ ba của bạn sai. Nó được cho là "" 2010-01-05 "<= ngày bắt đầu và" 2010-01-25 "> = end_date'. Xem http://stackoverflow.com/a/28802972/632951 để trực quan hóa. Điều kiện thứ ba hiện tại của bạn sẽ không bao giờ đánh giá, bởi vì điều kiện thứ nhất (và thứ hai) đã bao gồm nó. – Pacerier

Trả lời

110

tôi đã như một truy vấn với một ứng dụng lịch Tôi đã từng viết. Tôi nghĩ rằng tôi đã sử dụng một cái gì đó như thế này:

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

CẬP NHẬT này chắc chắn sẽ làm việc ((ns, ne, es, ee) = (NEW_START, NEW_END, existing_start, existing_end)):

  1. ns - ne - es - ee: không trùng lặp và không khớp nhau (vì ne < es)
  2. ns - es - ne - ee: trùng lặp và khớp với
  3. es - ns - ee - ne: overlaps and phù hợp với
  4. es - ee - ns - ne: không trùng lặp và không khớp nhau (vì ns> ee)
  5. es - ns - ne - ee: trùng lặp và khớp với
  6. ns - es - ee - ne: overlaps và phù hợp với

đây là một fiddle

+0

@Glide: Tôi nghĩ rằng nó sẽ hoạt động, câu trả lời cập nhật – soulmerge

+6

Hoạt động tuyệt vời !, nhưng tôi nghĩ @Pierre de LESPINAY đang tìm kiếm phạm vi bao gồm trong truy vấn của anh ấy: WHERE new_start <= existing_end AND new_end> = existing_start; –

+11

@OsvaldoM. Nếu anh ta thực sự, anh ta sẽ phàn nàn khoảng 2 năm trước… – 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') 

những 3 dòng khoản sql bao gồm 4 trường hợp trùng lặp được yêu cầu.

+3

bạn đời công việc tốt :) – Adrian

+3

Ngay cả khi OP dường như không tìm kiếm định nghĩa trùng lặp này, câu trả lời này là giải pháp tốt nhất cho vấn đề được mô tả bằng tên câu hỏi. Tôi đang tìm kiếm sự chồng chéo này, đó là sự trùng lặp thực sự. – Cec

+2

Tuyệt vời, đã cứu tôi một cơn đau đầu lớn. –

11

Câu trả lời của Lamy là tốt, nhưng bạn có thể tối ưu hóa nó nhiều hơn một chút.

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

Điều này sẽ bắt tất cả bốn trường hợp mà các phạm vi chồng lên nhau và loại trừ hai trường hợp không có.

+0

Có giải pháp nào khác ngoài giải pháp này và hai giải pháp khác ở trên không? – Pacerier

3

Tôi đã gặp phải sự cố tương tự. Vấn đề của tôi là ngừng đặt phòng giữa một phạm vi ngày bị chặn. Ví dụ, đặt phòng bị chặn đối với tài sản từ ngày 2 đến ngày 7 có thể. Tôi cần tìm bất kỳ loại ngày trùng lặp nào để phát hiện và ngừng đặt chỗ. Giải pháp của tôi tương tự như 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 

Hãy cho tôi biết nếu nó không hoạt động.

1

Cho hai khoảng thời gian tương tự (s1, e1) và (s2, e2) với s1 < e1 và s2 < e2
bạn có thể tính chồng chéo như thế này:

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 

Cũng sẽ làm việc nếu một khoảng thời gian là trong cái còn lại.

0

Gần đây tôi đã phải vật lộn với vấn đề tương tự và đến kết thúc với một bước đơn giản này (Điều này có thể không phải là một cách tiếp cận tốt hay bộ nhớ tiêu thụ) -

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) 
); 

này đã giúp tôi tìm ra mục với ngày chồng chéo các dãy.

Hy vọng điều này sẽ giúp ai đó.

Các vấn đề liên quan