2011-10-04 50 views
8

Tôi có một bảng có các cột start_date và end_date. Những gì chúng ta cần làm là Chọn tất cả mọi thứ và nhóm chúng theo các xung đột ngày cho mỗi Object_ID.Nhóm SQL theo các xung đột ngày

Xung đột ngày là thời điểm ngày bắt đầu và/hoặc ngày kết thúc của hàng vượt qua các hàng khác '. Ví dụ: dưới đây là một số ví dụ về xung đột:

Hàng 1 có ngày từ 1 đến ngày 5, Hàng 2 có từ ngày 2 đến ngày 3.

Hàng 1 có từ ngày 2 đến ngày 5, Hàng 2 có ngày từ 1 đến ngày 3.

Hàng 1 có từ ngày 2 đến ngày 5, Hàng 2 có từ ngày 3 đến ngày 6.

Hàng 1 có từ ngày 2 đến ngày 5, Hàng 2 có từ ngày 1 đến ngày 7.

Vì vậy, ví dụ, nếu chúng ta có một số dữ liệu mẫu (giả định con số này chỉ vài ngày của tháng vì đơn giản):

object_id | start_date | end_date | numconflicts 
1   | <na>  | <na>  | 2 
1   | 6   | 8  | 0 or null 
2   | 2   | 3  | 0 or null 
:

id | object_id | start_date | end_date 
1 | 1   | 1   | 5 
2 | 1   | 2   | 4 
3 | 1   | 6   | 8 
4 | 2   | 2   | 3 

gì tôi mong chờ để xem đây là

Và đối với Trường hợp thử nghiệm thứ hai, Dưới đây là một số dữ liệu mẫu:

id | object_id | start_date | end_date 
1 | 1   | 1   | 5 
2 | 1   | 2   | 4 
3 | 1   | 6   | 8 
4 | 2   | 2   | 3 
5 | 2   | 4   | 5 
6 | 1   | 2   | 3 
7 | 1   | 10   | 12 
8 | 1   | 11   | 13 

Và đối với Trường hợp thử nghiệm thứ hai, tôi mong đợi để xem là đầu ra:

object_id | start_date | end_date | numconflicts 
1   | <na>  | <na>  | 3 
1   | 6   | 8  | 0 or null 
2   | 2   | 3  | 0 or null 
2   | 4   | 5  | 0 or null 
1   | <na>  | <na>  | 2 

Vâng, tôi sẽ cần một số cách phân biệt nhóm thứ nhất và nhóm thứ hai (hàng đầu tiên và cuối cùng) nhưng tôi không hiểu rõ điều đó. Mục tiêu là để xem danh sách này và sau đó khi bạn nhấp vào một nhóm xung đột, bạn có thể xem tất cả các xung đột trong nhóm đó.

Suy nghĩ đầu tiên của tôi là thử một số mệnh đề GROUP BY CASE ... nhưng tôi chỉ bị bao quanh bởi chính nó.

Ngôn ngữ tôi đang sử dụng để gọi mysql là php. Vì vậy, nếu ai đó biết về một giải pháp vòng lặp php hơn là một truy vấn mysql lớn, tôi là tất cả các tai.

Xin cảm ơn trước.

Chỉnh sửa: Được thêm vào khóa chính để cung cấp ít nhầm lẫn hơn một chút.

Chỉnh sửa: Đã thêm trong trường hợp Kiểm tra 2 để cung cấp một số lý do khác.

+0

không, object_id không đề cập đến các id của bảng, nó chỉ đề cập đến một đối tượng vào một bảng khác nhau với id đó. Có nghĩa là, chỉ những hàng có object_id tương tự mới có thể có khả năng bị xung đột. – Peanut

+0

Chỉ là một ý nghĩ: Nếu bạn đang dự định các hàng xung đột có thể nhấp và mở rộng, việc đặt '' thay vì ngày thực tế dường như không có ý nghĩa gì nhiều. Nếu tôi là bạn, tôi có thể sử dụng 'MIN (start_date) AS start_date' và' MAX (end_date) AS end_date' cho các hàng xung đột. Bằng cách đó, người dùng có thể thấy ngay lập tức phạm vi ngày mà nhóm xung đột thuộc về, mà không phải nhấp/mở rộng phạm vi ngày đầu tiên. –

Trả lời

2

Truy vấn này thấy số lượng bản sao:

select od1.object_id, od1.start_date, od1.end_date, sum(od2.id is not null) as dups 
from object_date od1 
left join object_date od2 
    on od2.object_id = od1.object_id 
    and od2.end_date >= od1.start_date 
    and od2.start_date <= od1.end_date 
    and od2.id != od1.id 
group by 1,2,3; 

Bạn có thể sử dụng truy vấn này là cơ sở của một truy vấn cung cấp cho bạn chính xác những gì bạn yêu cầu (xem dưới đây để biết sản lượng).

select 
    object_id, 
    case dups when 0 then start_date else '<na>' end as start_date, 
    case dups when 0 then end_date else '<na>' end as end_date, 
    sum(dups) as dups 
from (
    select od1.object_id, od1.start_date, od1.end_date, sum(od2.id is not null) as dups 
    from object_date od1 
    left join object_date od2 
    on od2.object_id = od1.object_id 
    and od2.end_date >= od1.start_date 
    and od2.start_date <= od1.end_date 
    and od2.id != od1.id 
    group by 1,2,3) x 
group by 1,2,3; 

Lưu ý rằng tôi đã sử dụng cột id để phân biệt các hàng. Tuy nhiên, bạn có thể thay thế thử nghiệm của id không khớp với so sánh trên mỗi cột, tức là thay thế od2.id != od1.id bằng các kiểm tra mà mỗi cột khác không bằng nhau, nhưng điều đó sẽ yêu cầu chỉ mục duy nhất trên tất cả các cột khác có ý nghĩa và có id cột là một ý tưởng hay.

Dưới đây là một thử nghiệm sử dụng dữ liệu của bạn:

create table object_date (
    id int primary key auto_increment, 
    object_id int, 
    start_date int, 
    end_date int 
); 
insert into object_date (object_id, start_date, end_date) 
    values (1,1,5),(1,2,4),(1,6,8),(2,2,3); 

Sản lượng truy vấn đầu tiên khi chạy với dữ liệu mẫu này:

+-----------+------------+----------+------+ 
| object_id | start_date | end_date | dups | 
+-----------+------------+----------+------+ 
|   1 |   1 |  5 | 1 | 
|   1 |   2 |  4 | 1 | 
|   1 |   6 |  8 | 0 | 
|   2 |   2 |  3 | 0 | 
+-----------+------------+----------+------+ 

Sản lượng truy vấn thứ hai khi chạy với dữ liệu mẫu này:

+-----------+------------+----------+------+ 
| object_id | start_date | end_date | dups | 
+-----------+------------+----------+------+ 
|   1 | 6   | 8  | 0 | 
|   1 | <na>  | <na>  | 2 | 
|   2 | 2   | 3  | 0 | 
+-----------+------------+----------+------+ 
+0

chính xác nhóm của bạn theo mệnh đề là gì? 1,2,3 đến từ đâu? – Peanut

+0

@Peanut 'nhóm 1,2,3' là một cú pháp viết tắt cho' nhóm theo cột1, cột2, cột3' - trong trường hợp này là 'nhóm theo od1.object_id, od1.start_date, od1.end_date'. Đó là một phần của tiêu chuẩn SQL và hoạt động trên mọi cơ sở dữ liệu. Tôi thấy nó rất dễ đọc hơn và IMHO ít * dễ bị lỗi hơn, đặc biệt khi các cột được nhóm lại là các phép tính - nhiều DB yêu cầu bạn * lặp lại * phép tính trong nhóm, dẫn đến hình thức sao chép và do đó một nguồn tiềm năng của lỗi/lỗi – Bohemian

+0

Bạn trả lời là nhận được những gì tôi cần, nhưng không hoàn toàn ở đó. Tôi sẽ cập nhật chủ đề ngay bây giờ để giải thích thêm. – Peanut

0

Oracle: Điều này có thể được thực hiện với truy vấn con trong nhóm theo câu lệnh CASE.

https://forums.oracle.com/forums/thread.jspa?threadID=2131172

Mysql: Bạn có thể có một cái nhìn trong đó có tất cả các cuộc xung đột.

chọn a1.appt riêng biệt, a2.appt từ cuộc hẹn a1, cuộc hẹn a2 trong đó a1.start < a2.end và a1.end> a2.start.

và sau đó chỉ cần đếm (*) trên bảng đó.

0

giống như sau nên làm việc:

select T1.object_id, T1.start_date, T1.end_date, count(T1.object_id) as numconflicts 
from T1 
inner join T2 on T1.start_date between T2.start_date and T2.end_date 
inner join T3 on T1.end_date between T2.start_date and T2.end_date 
group by T1.object_id 

tôi có thể ra một chút, nhưng nó sẽ giúp bạn bắt đầu.

Sửa: thụt nó đúng

+0

Chỉ có 1 bảng, nhưng tôi có thể thấy nơi bắt nguồn T2 bằng cách nói tham gia bên trong (SELECT ....) khi T2 có thể áp dụng. – Peanut