powered by simpleCommunicator - 2.0.59     © 2025 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / MySQL [игнор отключен] [закрыт для гостей] / Подсчёт уникального количества дней в таблице с диапазонами дат
12 сообщений из 12, страница 1 из 1
Подсчёт уникального количества дней в таблице с диапазонами дат
    #39659920
magway_minsk
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Здравствуйте.
Пытаюсь найти решение для одной задачки: есть таблица, содержащая Имя, дату начала интервала и дату конца интервала.
Суть в том, что у человека с одним и тем же именем - может быть несколько записей, в которых даты могут пересекаться (собственно фото)


Нужно найти для каждого человека (в данном случае для одного Петрова) количество уникальных дней за все даты. То есть не учитывать наложение дат...

Если суммарное количество дней найти по всем записям элементарно - например
Код: sql
1.
SELECT `people_id`, SUM(DATEDIFF(end_date, `start_date`)) AS wd FROM test_days GROUP BY people_id

или
Код: sql
1.
2.
3.
4.
5.
SELECT SUM(days) total 
FROM 
(
	SELECT datediff(`end_date`, `start_date`) days FROM test_days
) AS get_days

то как исключить наложения я не додумался.

Если есть добрые люди - поделитесь идеей, пожалуйста.

Сам код таблицы
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
CREATE TABLE `test_days` (
  `people_id` varchar(100) DEFAULT NULL,
  `start_date` date DEFAULT NULL,
  `end_date` date DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


INSERT INTO `test_days` (`people_id`, `start_date`, `end_date`) VALUES
('Петров', '2018-06-05', '2018-06-09'),
('Петров', '2018-05-01', '2018-05-19'),
('Петров', '2018-05-06', '2018-05-19'),
('Петров', '2018-05-03', '2018-05-23');
...
Рейтинг: 0 / 0
Подсчёт уникального количества дней в таблице с диапазонами дат
    #39659937
982183
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Отсортируй по дате начала периода.
Отнимай start_date от end_date предыдущей записи.
Если результат отрицательный, отнимай его от той суммы, которую получил "элементарно" ранее.
...
Рейтинг: 0 / 0
Подсчёт уникального количества дней в таблице с диапазонами дат
    #39659943
982183
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Вру. не будет работать.
периоды переплетаются
...
Рейтинг: 0 / 0
Подсчёт уникального количества дней в таблице с диапазонами дат
    #39659944
982183
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Посмотри в поиске "пересечение периодов"
часто подобная задача всплывает.
...
Рейтинг: 0 / 0
Подсчёт уникального количества дней в таблице с диапазонами дат
    #39659972
Фотография Akina
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Поскольку количество пересекающихся периодов неизвестно заранее и не ограничено формально ничем, решение может быть только рекурсивным. Если версия сервера - 8, то там есть рекурсивные СТЕ, и задача элементарна. Если нет - то придётся делать псевдорекурсию. На отсортированной по ФИО и началу интервала таблице в подзапросе двумя переменными тащить текущие начало и конец объединённого периода.А в основном запросе группировать и брать максимальное время окончания.
...
Рейтинг: 0 / 0
Подсчёт уникального количества дней в таблице с диапазонами дат
    #39659985
982183
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ну можно создать отдельную таблицу, где будут все даты с минимального начала, по максимальный конец.
Пометить там используемые периоды, а потом сформировать периоды из неё.
...
Рейтинг: 0 / 0
Подсчёт уникального количества дней в таблице с диапазонами дат
    #39660020
Фотография Щукина Анна
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
magway_minsk,

у вас - классическая задача на "островки". Решается через два вложенных подзапроса с [not] exists предикатом.
...
Рейтинг: 0 / 0
Подсчёт уникального количества дней в таблице с диапазонами дат
    #39660038
Фотография Щукина Анна
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
magway_minsk,

Словесное описание алгоритма действий:
1) Для каждого пользователя объединяем все маленькие пересекающие между собой интервалы в сплошные не пересекающиеся между собой "мегаинтервалы"

2) Для каждого пользователя определяем длительность каждого полученного "мегаинтервала"

3) Для каждого пользователя определяем сумму длительностей всех "мегаинтервалов"

Пример реализации:
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
select people_id
     , sum(days) as days
  from (-- Список длительностей "монолитных" периодов в разрезе пользователя:
         select all_start_date.people_id
              , datediff(min(all_end_date.end_date), all_start_date.start_date) as days
           from  (-- Начальные точки "монолитных" периодов
                   select start_date
                        , s1.people_id
                     from test_days s1
                    where not exists 
                            (
                               select null
                                 from test_days s2
                                where s2.start_date < s1.start_date
                                  and s2.end_date >= s1.start_date
								  and s1.people_id = s2.people_id
                            )
                 ) all_start_date,
                 (-- Конечные точки "монолитных" периодов
                   select end_date
                        , s1.people_id
                     from test_days s1
                    where not exists 
                            (
                               select null
                                 from test_days s2
                                where s2.end_date > s1.end_date
                                  and s2.start_date <= s1.end_date
								  and s1.people_id = s2.people_id
                            )
                 ) all_end_date
           where all_start_date.people_id = all_end_date.people_id
             and all_start_date.start_date <= all_end_date.end_date
           group by all_start_date.people_id, all_start_date.start_date
       ) v
 group by people_id
 order by people_id

...
Рейтинг: 0 / 0
Подсчёт уникального количества дней в таблице с диапазонами дат
    #39661184
magway_minsk
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Есть еще пара вариантов. Один подсказал пользователь стэка:
Код: sql
1.
2.
3.
4.
5.
6.
7.
create table seqnum(X int not null);
-- Первые 8 записей
insert into seqnum values(0),(1),(2),(3),(4),(5),(6),(7);
-- И еще 512
insert into seqnum
select s1.x*64+s2.x*8+s3.x+8
  from seqnum s1, seqnum s2, seqnum s3;


Код: sql
1.
2.
3.
4.
select d.people_id, count(distinct d.start_date + interval s.x day) days
  from test_days d, seqnum s
 where s.x<=DATEDIFF(end_date, start_date)
 group by d.people_id



Второй родил я. не самый крутой , вообще не крутой, но зато сам))

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
CREATE DEFINER=`root`@`%` FUNCTION `p2` (`name` VARCHAR(255)) RETURNS INT(10) BEGIN

        DECLARE d1 date;
        DECLARE d2 date;
        DECLARE prev_d1 date;
        DECLARE prev_d2 date;
        DECLARE done INT DEFAULT 0;
        DECLARE summ_days INT DEFAULT 0;
        DEClARE cur CURSOR FOR 
            SELECT start_date, end_date FROM test_days WHERE people_id = name         ORDER BY start_date ;
        DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done = 1;
        OPEN cur;
        SET summ_days:=0;
        FETCH cur INTO prev_d1,prev_d2;
        REPEAT
            FETCH cur INTO d1,d2;
                IF NOT done THEN
                    IF d2 >= prev_d2 AND d1 <= prev_d2 THEN

                        SET summ_days:= summ_days + DATEDIFF(d2, prev_d1);
                        SET prev_d2:= d2;
                       ELSEIF d1 > prev_d2 THEN
                        IF summ_days = 0 THEN
                            SET summ_days:= summ_days + DATEDIFF(d2, d1) + DATEDIFF(prev_d2, prev_d1);
                            SET prev_d1:= d1;
                            SET prev_d2:= d2;
                        ELSE
                            SET summ_days:= summ_days + DATEDIFF(d2, d1);
                            SET prev_d1:= d1;
                            SET prev_d2:= d2;
                        END IF;
                    END IF;
                END IF;


        UNTIL done  END REPEAT;

       CLOSE cur;

       RETURN summ_days;

    END$$

    DELIMITER ;



Теперь вызов:
Код: sql
1.
SELECT `people_id`, p2(`people_id`) FROM test_days GROUP BY `people_id`



Спасибо всем кто откликнулся!
...
Рейтинг: 0 / 0
Подсчёт уникального количества дней в таблице с диапазонами дат
    #39661579
magway_minsk
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Подправил свою функцию...
Не верно считала...

Поправка на ветер:

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
	
  BEGIN
	
    DECLARE d1 date;
    DECLARE d2 date;
    DECLARE prev_d1 date;
    DECLARE prev_d2 date;
    DECLARE done INT DEFAULT 0;
    DECLARE summ_days INT DEFAULT 0;
    DEClARE cur CURSOR FOR 
		SELECT start_date, end_date FROM test_days WHERE people_id = name 		ORDER BY start_date ;
	DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done = 1;
    OPEN cur;
    SET summ_days:=0;
    FETCH cur INTO prev_d1,prev_d2;
    REPEAT
    	FETCH cur INTO d1,d2;
        	IF summ_days = 0 THEN
                	SET summ_days =  DATEDIFF(prev_d2, prev_d1) + 1;         
            END IF;
        	IF NOT done THEN
            	                
                IF d1 > prev_d2 THEN
                	SET summ_days = summ_days + DATEDIFF(d2, d1) + 1;
                    SET prev_d1 = d1;
                    SET prev_d2 = d2;
                ELSE IF d2 > prev_d2 AND d1 <= prev_d2 THEN
                	SET summ_days = summ_days + DATEDIFF(d2, prev_d2);
                    SET prev_d2 = d2;
                    END IF;
                END IF;
               
            END IF;
           
         
	UNTIL done  END REPEAT;

   CLOSE cur;

   RETURN summ_days;

END


...
Рейтинг: 0 / 0
Подсчёт уникального количества дней в таблице с диапазонами дат
    #39661610
miksoft
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
982183Ну можно создать отдельную таблицу, где будут все даты с минимального начала, по максимальный конец.
Пометить там используемые периоды, а потом сформировать периоды из неё.Я бы тоже на основе опорной таблицы-календаря сделал. Только не метить в ней ничего, а сджойнить и сделать COUNT(DISTINCT дата).
...
Рейтинг: 0 / 0
Подсчёт уникального количества дней в таблице с диапазонами дат
    #39661683
982183
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ему в результате надо получить периоды "с_по"
Не вижу алгоритма без предварительной пометки.
...
Рейтинг: 0 / 0
12 сообщений из 12, страница 1 из 1
Форумы / MySQL [игнор отключен] [закрыт для гостей] / Подсчёт уникального количества дней в таблице с диапазонами дат
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]