[ Поиск ] - [ Пользователи ] - [ Календарь ]
Полная Версия: Группировка пересекающихся интервалов
kaww
Всем привет!
Имеем таблицу, которая содержит временные интервалы вида:
+---------------------+---------------------+
| date_from | date_to |
+---------------------+---------------------+
| 2017-01-01 00:00:00 | 2017-01-01 00:00:10 |
| 2017-01-01 00:00:11 | 2017-01-01 00:10:00 |
| 2017-01-01 00:10:20 | 2017-01-01 00:30:00 |
| 2017-01-01 00:29:00 | 2017-01-01 00:30:10 |
| 2017-01-01 01:00:00 | 2017-01-01 02:00:00 |
| 2017-01-01 02:00:00 | 2017-01-01 04:00:00 |
| 2017-01-01 05:00:00 | 2017-01-01 08:00:00 |
+---------------------+---------------------+

необходимо получить из нее нечто подобное
+---------------------+---------------------+
| date_from | date_to |
+---------------------+---------------------+
| 2017-01-01 00:00:00 | 2017-01-01 00:00:10 |
| 2017-01-01 00:00:11 | 2017-01-01 00:10:00 |
| 2017-01-01 00:10:20 | 2017-01-01 00:30:10 |
| 2017-01-01 01:00:00 | 2017-01-01 04:00:00 |
| 2017-01-01 05:00:00 | 2017-01-01 08:00:00 |
+---------------------+---------------------+

т.е. сгруппировать по пересекающимся интервалам, что из 2017-01-01 01:00:00-2017-01-01 02:00:00 и 2017-01-01 02:00:00- 2017-01-01 04:00:00 получилось 2017-01-01 01:00:00-2017-01-01 04:00:00
bestxp
любопытно даже, такой кошмар помню на PHP разруливал, интересно на SQL может кто покажет, но кмк без хранимки никак особенно если они не в стыке соединяються, а пересекаються о_О
kaww
Получилось это. Пока не до конца уверен, что работает верно (И действительно, работает неверно). Может что-нибудь лучше удастся найти. Да и join пришлось заюзать:
SELECT 
MIN(date_from), MAX(date_to)
FROM (
SELECT
IF
(MIN(i1.date_from) <= MIN(IFNULL(i2.date_from, i1.date_from)), MIN(i1.date_from), MIN(i2.date_from)) AS date_from,
IF(MAX(i1.date_to) >= MAX(IFNULL(i2.date_to, i1.date_to)), MAX(i1.date_to), MAX(i2.date_to)) AS date_to
FROM intervals AS i1
LEFT JOIN intervals AS i2 ON i1.id>i2.id AND (i1.date_from BETWEEN i2.date_from AND i2.date_to OR i2.date_from BETWEEN i1.date_from AND i1.date_to)
GROUP BY i1.date_from) AS tmp
GROUP BY date_from

http://sqlfiddle.com/#!9/f244f2/2
kaww
Вариант на php (который тоже оказался не рабочим):
$data = [
[
'from' => '2017-01-01 00:00:00', 'to' => '2017-01-01 00:00:10'],
[
'from' => '2017-01-01 00:00:11', 'to' => '2017-01-01 00:10:00'],
[
'from' => '2017-01-01 00:10:20', 'to' => '2017-01-01 00:30:00'],
[
'from' => '2017-01-01 00:29:00', 'to' => '2017-01-01 00:30:10'],
[
'from' => '2017-01-01 01:00:00', 'to' => '2017-01-01 02:00:00'],
[
'from' => '2017-01-01 02:00:00', 'to' => '2017-01-01 04:00:00'],
[
'from' => '2017-01-01 05:00:00', 'to' => '2017-01-01 08:00:00'],
];

$intervals = [];
foreach ($data as $item) {
$from = strtotime($item['from']);
$to = strtotime($item['to']);
$f = false;
foreach ($intervals as &$interval) {
if (($from >= $interval['from'] && $from <= $interval['to'])
|| (
$to >= $interval['from']) && $to <= $interval['to']) {
if ($interval['from'] > $from) {
$interval['from'] = $from;
}
if ($interval['to'] < $to) {
$interval['to'] = $to;
}
$f = true;
break;
}
}

if (!$f) {
$intervals[] = ['from' => $from, 'to' => $to];
}
}

unset($interval);
//Debug output:
foreach ($intervals as $interval) {
echo date('Y-m-d H:i:s', $interval['from']), ' - ', date('Y-m-d H:i:s', $interval['to']), PHP_EOL;
}
Быстрый ответ:

 Графические смайлики |  Показывать подпись
Здесь расположена полная версия этой страницы.
Invision Power Board © 2001-2025 Invision Power Services, Inc.