문제

그래서 이것은 여기서 첫 번째 질문 중 하나 였지만 약간의 변형이 있습니다.

따라서 데이터베이스에 일정이있는 두 사람이 있습니다. 일정은 단순히 시작 시간, 종료 시간 및 두 사용자의 다양한 이벤트/약속에 대한 설명을 기록합니다.

페르소나는 약속을 Personb과 거래하고 싶어합니다. 나는 사람과 페르소나가 교환 할 수있는 모든 시간을 반환 할 MySQL 쿼리를 원합니다.

원래 쿼리의 매개 변수는 페르소나와 겹치는 사람의 임명을 버려야했고 Personb의 약속은 페르소나가 교환하고자하는 약속과 동일한 길이가되어야했습니다. 시간 산술/지오메트리에 대한 훌륭한 조언을 받았으며 필요한 결과를 얻는 데 도움이되었습니다.

이제 약속의 길이가 같지 않도록 1-1 매개 변수를 변경하고 싶습니다. 따라서 페르소나가 월요일 아침 약속 (오전 10시 - 오전 11시 30 분)을 교환하려면 다음과 같습니다.

  • 페르소나 약속 중 하나 인 Personb의 약속 제외
  • 페르소나 약속 밖에있는 Personb의 약속을 포함
  • 페르소나가 무료 인 동안 인 Personb의 약속의 일부를 포함 시키지만 자유 부분 만 표시하십시오.

따라서 페르소나가 위의 약속을 교체하고 싶다면 (다시, 월요일 오전 10시 - 오전 11시 30 분), 페르소나는 화요일 오후 1 시부 터 오후 3 시까 지 약속을 잡고 있으며 Personb은 화요일 12시에 약속을 잡습니다. 00 PM에서 오후 4 시까 지 쿼리가 반환됩니다.

Possible_Swaps
==============
userID  | Start             | End             | Description
PersonB | Tuesday, 12:00 PM | Tuesday 1:00 PM | Cooking
PersonB | Tuesday,  4:00 PM | Tuesday 5:00 PM | Cooking

다른 가능성 외에도. 데이터베이스에서 기대하기가 너무 많습니까? 그렇다면 적어도 겹치지 만 PHP 스크립트가 그들을 다룰 수 있도록 시간이 양쪽에 매달려있는 방법에 대한 제안이 있습니까?


Searlea의 요청에 따라 여기에 조금 더 문맥이 있습니다.

나는 계속 약속을 말했지만 "작업 교대"에서와 같이 실제로 "직업"을 의미한다고 생각합니다. 페르소나와 사람은 같은 사무실에서 일합니다. Vcalendar에서, 작업 변화는 일반적으로 "이벤트"라고하지만 때때로 "약속"이라고하며, 두 사람이 박람회에가는 것처럼 들리기 때문에 후자와 함께 갔다.

따라서 페르소나는 월요일 오전 10 시부 터 11시 30 분까지 식기 세척 교대가 있습니다. Personb는 화요일 오후 12 시부 터 오후 5 시까 지 요리합니다. 페르소나는 월요일에 마을을 떠나기 전에 동생을보고 싶어합니다. 그는 월요일 아침을 떠나고 싶었지만 한 시간의 교대를 시작했습니다.

그래서 내 오래된 모델에서 (자랐습니다 나의 첫 번째 질문 여기), 나는 겹치지 않았고 교대 근무가 제 시간에 동등한 곳을 찾고있었습니다. 그러나 그것은 두 가지 문제가 있습니다.

  1. 화요일에 2 시간의 교대를 덮을 사람이 필요하고 목요일에 4 시간 동안 일하고 Joe는 목요일에 8 시간 동안 일하고 있습니다. 2 시간의 시간을 교환 할 수 있었고 조금 일찍 떠날 수 있었고 조금 머물 수 있습니다. 나중에.

  2. 2 시간 간의 교대가 있지만 제 시간에 공항으로 공항으로 만들기 위해 한 시간을 기꺼이 교환 할 것입니다. 그런 다음 주 후반에 나보다 1 시간 일찍오고 있는지 알고 싶습니다. 그의 변화의 일부를 취하십시오.

짧은 이야기 (너무 늦었습니다), 나는 분명히 알려진 것을 원한다 상대적 보완 페르소나의 개인 변이 (기본적으로 개인이 작동하고 페르소나가 다른 시점에서 교대가 겹치는지 여부에 관계없이 그렇지 않습니다.)

이상적으로, 나는 Personb가 작동하고 페르소나가 작동하지 않았던 비트 (위에서 언급 한 2 시간의 1 시간 교대)와 전체 교대 (특별한 태그가 포함 된 특수 태그 포함)를 포함한 일련의 결과를 얻을 것입니다. 전체) 그래서 페르소나는 그가 교대의 일부를 덮고 있다는 것을 알게되었고 혼란스러워하지 않고 Personb가 2 시간의 교대 근무를하고 있다고 생각합니다.

이것은 모두 조금 복잡하게 들리기 시작합니다. 기본적으로 나는 Personb의 변화가 파란색이되기를 원하고 Persona의 변화는 노란색이되기를 원하며 데이터베이스가 녹색이 아닌 모든 부품을 반환하기를 원합니다.

도움이 되었습니까?

해결책

SELECT * 
  FROM schedule AS s1
WHERE
  s1.user = 'Ondra'
AND
NOT EXISTS ( 
  SELECT * FROM schedule AS s2 
  WHERE
    s2.user = 'Zizka'
    AND (
      s2.start BETWEEN s1.start AND s1.end 
      OR
      s2.end BETWEEN s1.start AND s1.end 
      OR 
      s1.start > s2.start AND s1.end < s2.end 
    )
)

이것은 Zizka의 일기에 틈새에 들어갈 수있는 Ondra의 사건을 선택합니다.

편집 : 원래 교차로 였지만 상대 보완을 원한다면 충분합니다.

다른 팁

허락하다 $shift_id 사용자가 교체하고자하는 변화의 ID가 되십시오.

select swappable.shift_id, swappable.user_id, swappable.description,
    FROM_UNIXTIME(swappable.shiftstart) as start,
    FROM_UNIXTIME(swappable.shiftend) as end,
    (swappable.shiftend - swappable.shiftstart) -
        sum(coalesce(least(conflict.shiftend, swappable.shiftend) -
            greatest(conflict.shiftstart, swappable.shiftstart), 0))
        as swaptime,
    group_concat(conflict.shift_id) as conflicts,
    group_concat(concat(FROM_UNIXTIME(conflict.shiftstart), ' - ',
        FROM_UNIXTIME(conflict.shiftend))) as conflict_times
from shifts as problem
join shifts as swappable on swappable.user_id != problem.user_id
left join shifts as conflict on conflict.user_id = problem.user_id
    and conflict.shiftstart < swappable.shiftend
    and conflict.shiftend > swappable.shiftstart
where problem.shift_id = 1
group by swappable.shift_id
having swaptime > 0;

테스트 : :

CREATE TABLE `shifts` (
  `shift_id` int(10) unsigned NOT NULL auto_increment,
  `user_id` varchar(20) NOT NULL,
  `shiftstart` int unsigned NOT NULL,
  `shiftend` int unsigned NOT NULL,
  `description` varchar(32) default NULL,
  PRIMARY KEY  (`shift_id`)
);

insert  into `shifts`(`shift_id`,`user_id`,`shiftstart`,`shiftend`,`description`) values (1,'april', UNIX_TIMESTAMP('2009-04-04 10:00:00'),UNIX_TIMESTAMP('2009-04-04 12:00:00'),'Needs to be swapped');
insert  into `shifts`(`shift_id`,`user_id`,`shiftstart`,`shiftend`,`description`) values (2,'bill',  UNIX_TIMESTAMP('2009-04-04 10:30:00'),UNIX_TIMESTAMP('2009-04-04 11:30:00'),'Inside today');
insert  into `shifts`(`shift_id`,`user_id`,`shiftstart`,`shiftend`,`description`) values (3,'casey', UNIX_TIMESTAMP('2009-04-04 12:00:00'),UNIX_TIMESTAMP('2009-04-04 14:00:00'),'Immediately after today');
insert  into `shifts`(`shift_id`,`user_id`,`shiftstart`,`shiftend`,`description`) values (4,'casey', UNIX_TIMESTAMP('2009-04-04 08:00:00'),UNIX_TIMESTAMP('2009-04-04 10:00:00'),'Immediately before today');
insert  into `shifts`(`shift_id`,`user_id`,`shiftstart`,`shiftend`,`description`) values (5,'david', UNIX_TIMESTAMP('2009-04-04 11:00:00'),UNIX_TIMESTAMP('2009-04-04 15:00:00'),'Partly after today');

insert  into `shifts`(`shift_id`,`user_id`,`shiftstart`,`shiftend`,`description`) values (6,'april', UNIX_TIMESTAMP('2009-04-05 10:00:00'),UNIX_TIMESTAMP('2009-04-05 12:00:00'),'Tommorow');
insert  into `shifts`(`shift_id`,`user_id`,`shiftstart`,`shiftend`,`description`) values (7,'bill',  UNIX_TIMESTAMP('2009-04-05 09:00:00'),UNIX_TIMESTAMP('2009-04-05 11:00:00'),'Partly before tomorrow');
insert  into `shifts`(`shift_id`,`user_id`,`shiftstart`,`shiftend`,`description`) values (8,'casey', UNIX_TIMESTAMP('2009-04-05 10:00:00'),UNIX_TIMESTAMP('2009-04-05 12:00:00'),'Equals tomorrow');
insert  into `shifts`(`shift_id`,`user_id`,`shiftstart`,`shiftend`,`description`) values (9,'david', UNIX_TIMESTAMP('2009-04-05 10:30:00'),UNIX_TIMESTAMP('2009-04-05 11:30:00'),'Inside tomorrow');

insert  into `shifts`(`shift_id`,`user_id`,`shiftstart`,`shiftend`,`description`) values (10,'april',UNIX_TIMESTAMP('2009-04-11 10:00:00'),UNIX_TIMESTAMP('2009-04-11 12:00:00'),'Next week');
insert  into `shifts`(`shift_id`,`user_id`,`shiftstart`,`shiftend`,`description`) values (11,'april',UNIX_TIMESTAMP('2009-04-11 12:00:00'),UNIX_TIMESTAMP('2009-04-11 14:00:00'),'Second shift');
insert  into `shifts`(`shift_id`,`user_id`,`shiftstart`,`shiftend`,`description`) values (12,'bill', UNIX_TIMESTAMP('2009-04-11 11:00:00'),UNIX_TIMESTAMP('2009-04-11 13:00:00'),'Overlaps two');
insert  into `shifts`(`shift_id`,`user_id`,`shiftstart`,`shiftend`,`description`) values (13,'casey',UNIX_TIMESTAMP('2009-04-11 17:00:00'),UNIX_TIMESTAMP('2009-04-11 19:00:00'),'No conflict');

insert  into `shifts`(`shift_id`,`user_id`,`shiftstart`,`shiftend`,`description`) values (14,'april',UNIX_TIMESTAMP('2009-05-04 10:00:00'),UNIX_TIMESTAMP('2009-05-04 12:00:00'),'Next month');
insert  into `shifts`(`shift_id`,`user_id`,`shiftstart`,`shiftend`,`description`) values (15,'april',UNIX_TIMESTAMP('2009-05-04 13:00:00'),UNIX_TIMESTAMP('2009-05-04 15:00:00'),'After break');
insert  into `shifts`(`shift_id`,`user_id`,`shiftstart`,`shiftend`,`description`) values (16,'bill', UNIX_TIMESTAMP('2009-05-04 11:00:00'),UNIX_TIMESTAMP('2009-05-04 14:00:00'),'Middle okay');

insert  into `shifts`(`shift_id`,`user_id`,`shiftstart`,`shiftend`,`description`) values (17,'april',UNIX_TIMESTAMP('2010-04-04 10:00:00'),UNIX_TIMESTAMP('2010-04-04 11:00:00'),'Next year');
insert  into `shifts`(`shift_id`,`user_id`,`shiftstart`,`shiftend`,`description`) values (18,'april',UNIX_TIMESTAMP('2010-04-04 11:30:00'),UNIX_TIMESTAMP('2010-04-04 12:00:00'),'After break');
insert  into `shifts`(`shift_id`,`user_id`,`shiftstart`,`shiftend`,`description`) values (19,'april',UNIX_TIMESTAMP('2010-04-04 12:30:00'),UNIX_TIMESTAMP('2010-04-04 13:30:00'),'Third part');
insert  into `shifts`(`shift_id`,`user_id`,`shiftstart`,`shiftend`,`description`) values (20,'bill', UNIX_TIMESTAMP('2010-04-04 10:30:00'),UNIX_TIMESTAMP('2010-04-04 13:00:00'),'Two parts okay');

결과:

'shift_id', 'user_id', 'description',              'start',               'end',                 'swaptime', 'conflicts', 'conflict_times'
 '3',       'casey',   'Immediately after today',  '2009-04-04 12:00:00', '2009-04-04 14:00:00', '7200',       NULL,       NULL
 '4',       'casey',   'Immediately before today', '2009-04-04 08:00:00', '2009-04-04 10:00:00', '7200',       NULL,       NULL
 '5',       'david',   'Partly after today',       '2009-04-04 11:00:00', '2009-04-04 15:00:00', '10800',     '1',        '2009-04-04 10:00:00 - 2009-04-04 12:00:00'
 '7',       'bill',    'Partly before tomorrow',   '2009-04-05 09:00:00', '2009-04-05 11:00:00', '3600',      '6',        '2009-04-05 10:00:00 - 2009-04-05 12:00:00'
'13',       'casey',   'No conflict',              '2009-04-11 17:00:00', '2009-04-11 19:00:00', '7200',       NULL,       NULL
'16',       'bill',    'Middle okay',              '2009-05-04 11:00:00', '2009-05-04 14:00:00', '3600',      '15,14',    '2009-05-04 13:00:00 - 2009-05-04 15:00:00,2009-05-04 10:00:00 - 2009-05-04 12:00:00'
'20',       'bill',    'Two parts okay',           '2010-04-04 10:30:00', '2010-04-04 13:00:00', '3600',      '19,18,17', '2010-04-04 12:30:00 - 2010-04-04 13:30:00,2010-04-04 11:30:00 - 2010-04-04 12:00:00,2010-04-04 10:00:00 - 2010-04-04 11:00:00'

이는 총 시간 (초)이 교체 가능한 양을 포함하여 모든 부분을 교체 할 수있는 모든 교대를 보여줍니다. 마지막 열, conflict_times, 스와핑 사용자가 이미 작업 할 예정인 시간을 보여줍니다. 응용 프로그램이 이용 가능한 시간을 쉽게 추출 할 수 있어야합니다. MySQL에서는 가능하지만 매우 까다 롭습니다.

겹치는 부분을 제외한 두 개의 다른 사용자의 모든 간격을 반환합니다.

테이블 및 테스트 데이터

CREATE TABLE IF NOT EXISTS `shifts` (
  `id` int(11) NOT NULL auto_increment,
  `name` varchar(1) NOT NULL,
  `start` datetime NOT NULL,
  `end` datetime NOT NULL,
  PRIMARY KEY  (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=12 ;

INSERT INTO `shifts` (`id`, `name`, `start`, `end`) VALUES
(1, 'a', '2000-01-01 01:00:00', '2000-01-01 03:00:00'),
(2, 'a', '2000-01-01 06:00:00', '2000-01-01 07:30:00'),
(3, 'b', '2000-01-01 02:00:00', '2000-01-01 04:00:00'),
(4, 'b', '2000-01-01 05:00:00', '2000-01-01 07:00:00'),
(5, 'a', '2000-01-01 08:00:00', '2000-01-01 11:00:00'),
(6, 'b', '2000-01-01 09:00:00', '2000-01-01 10:00:00'),
(7, 'a', '2000-01-01 12:00:00', '2000-01-01 13:00:00'),
(8, 'b', '2000-01-01 14:00:00', '2000-01-01 14:30:00'),
(9, 'a', '2000-01-01 16:00:00', '2000-01-01 18:00:00'),
(10, 'a', '2000-01-01 19:00:00', '2000-01-01 21:00:00'),
(11, 'b', '2000-01-01 17:00:00', '2000-01-01 20:00:00');

시험 결과

        id  name    start           end
        1   a   2000-01-01 01:00:00 2000-01-01 02:00:00
        3   b   2000-01-01 03:00:00 2000-01-01 04:00:00
        4   b   2000-01-01 05:00:00 2000-01-01 06:00:00
        2   a   2000-01-01 07:00:00 2000-01-01 07:30:00
        5   a   2000-01-01 10:00:00 2000-01-01 11:00:00
        7   a   2000-01-01 12:00:00 2000-01-01 13:00:00
        8   b   2000-01-01 14:00:00 2000-01-01 14:30:00
        9   a   2000-01-01 16:00:00 2000-01-01 17:00:00
        11  b   2000-01-01 18:00:00 2000-01-01 19:00:00
        10  a   2000-01-01 20:00:00 2000-01-01 21:00:00

해결책

MySQL의 기능을 사용하여 사용자 정의 변수를 사용하여 다음 쿼리를 사용하여 목표를 달성했습니다.

SET @inA=0, @inB=0, @lastAstart = 0, @lastBstart = 0, @lastAend = 0, @lastBend = 0;
SELECT id,name,start,end FROM (
    SELECT 
        id,name,
        IF(name='a',
          IF(UNIX_TIMESTAMP(start) > @lastBend, start, FROM_UNIXTIME(@lastBend)),
          IF(UNIX_TIMESTAMP(start) > @lastAend, start, FROM_UNIXTIME(@lastAend))
        ) as start,
        IF(name='a',
          IF(@inB,FROM_UNIXTIME(@lastBstart),end),
          IF(@inA,FROM_UNIXTIME(@lastAstart),end)
        )  as end,
        IF(name='a',
          IF(@inB AND (@lastBstart < @lastAstart), 1, 0),
          IF(@inA AND (@lastAstart < @lastBstart), 1, 0)
        ) as fullyEnclosed,
          isStart,
          IF(name='a',@inA:=isStart,0), 
          IF(name='b',@inB:=isStart,0), 
          IF(name='a',IF(isStart,@lastAstart:=t,@lastAend:=t),0), 
          IF(name='b',IF(isStart,@lastBstart:=t,@lastBend:=t),0)
    FROM (
            (SELECT *, UNIX_TIMESTAMP(start) as t, 1 as isStart FROM `shifts` WHERE name IN ('a', 'b'))
        UNION ALL 
            (SELECT *, UNIX_TIMESTAMP(end) as t, 0 as isStart FROM `shifts` WHERE name IN ('a', 'b'))
        ORDER BY t
    ) as sae
) AS final WHERE NOT isStart AND NOT fullyEnclosed;

기본 아이디어는 모든 레코드가 두 번 표시되도록 시간별로 두 번 정렬 된 테이블을 나열하는 것입니다. 한 번 시작 시간과 종료 시간. 그런 다음 사용자 정의 변수를 사용하여 상태를 추적하는 동안 레코드를 추적하고 시작 시간과 중첩 간격에 맞게 조정 된 시작 시간과 종료 시간만으로 '종료 시간'레코드 만 반환합니다.

가정

유일한 가정은 사람 X 간격이 같은 사람의 다른 간격과 겹치지 않는다는 것입니다.

행동

몇 가지 사례와 결과 :

<  (   >   )
<  >   (   )

( < )  ( > )
( ) <  > ( )

<  (   )   >    // for this and similar cases only last part of interval is returned
       <   >

(   <  )   (   )  (  )  (   >   )  // like so
(   )                <  >   (   )

경고

MySQL 서버가 사용자 정의 변수에 보관 된 DateTime을 비교할 수 없었기 때문에 Unix Timestamp를 사용해야했습니다.

장단점

결합없이 단일 패스로 작업을 수행하므로 O (N) 시간이 걸립니다. 사람의 간격 부분을 모두 검색 할 수는 없습니다.

참조를 위해 최근에 내가 사용한 코드가 snipped. 겹치는 날짜 범위를 확인하는 데 사용할 수 있습니다. Ruby on Rails로 작성되었지만 아이디어 (SQL 문)은 다른 언어로 쉽게 번역 될 수 있습니다).

  class Absence
    named_scope :overlaps, lambda { |start, ende| { 
      :conditions =>
          ["   absences.start_date BETWEEN :start AND :end " +
           "OR absences.end_date   BETWEEN :start AND :end " +
           "OR :start BETWEEN absences.start_date AND absences.end_date " +
           "OR :end BETWEEN absences.start_date AND absences.end_date ",
              {:start => start, :end => ende } ]
      }}
  end

이름이 지정된 스코프와 같이이 범위는 다른 스코프와 함께 재사용 할 수 있습니다.

user = User.find(...)
today = Date.today
confirmed_absences = user.absences.confirmed.overlaps(today.beginning_of_month, today.end_of_month).count
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top