简体   繁体   中英

Ordering within a MySQL group

I have two tables which are joined - one holds schedules and the other holds actual worked times.

This works fine if a given user only has a single schedule on a day but when they have more than one schedule I cannot get the query to match up the "right" slot to the right time.

I am beginning to think the only way to do this is to allocate the time to the schedule when the clock event happens but that is going to be a big rewrite so I am hoping there is a way in MySQL.

As this is inside a third party application, I am limited in what I can do to the query - I can modify the basics like from, group, joins etc and I can add aggregates to the fields (I have toyed with using min/max on the times). However, if the only way is to write a hugely complex query especially within the field selections then this system simply doesn't give me that option.

Schedule table:

CREATE TABLE `schedule` (
  `id` int(11) NOT NULL,
  `user_id` int(11) NOT NULL,
  `date` date NOT NULL,
  `start_time` time NOT NULL,
  `end_time` time NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

--
-- Dumping data for table `schedule`
--

INSERT INTO `schedule` (`id`, `user_id`, `date`, `start_time`, `end_time`) VALUES
(1, 1, '2019-07-07', '08:00:00', '12:00:00'),
(2, 1, '2019-07-07', '16:00:00', '22:00:00'),
(3, 1, '2019-07-06', '10:00:00', '18:00:00');

Time table

CREATE TABLE `time` (
  `id` int(11) NOT NULL,
  `user_id` int(11) NOT NULL,
  `date` date NOT NULL,
  `start_time` time NOT NULL,
  `end_time` time NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

--
-- Dumping data for table `time`
--

INSERT INTO `time` (`id`, `user_id`, `date`, `start_time`, `end_time`) VALUES
(1, 1, '2019-07-07', '08:00:00', '12:00:00'),
(2, 1, '2019-07-07', '16:00:00', '22:00:00'),
(3, 1, '2019-07-06', '10:00:00', '18:00:00');

Current query

select 
t.date as date, t.user_id, 
s.start_time as schedule_start, 
s.end_time as schedule_end, 
t.start_time as actual_start, 
t.end_time as actual_end 
from time t 
left join schedule s on 
t.user_id=s.user_id and t.date=s.date 
group by t.date, t.start_time

Current output

== Dumping data for table s

|2019-07-06|1|10:00:00|18:00:00|10:00:00|18:00:00
|2019-07-07|1|08:00:00|12:00:00|08:00:00|12:00:00
|2019-07-07|1|08:00:00|12:00:00|16:00:00|22:00:00

Desired output

== Dumping data for table s

|2019-07-06|1|10:00:00|18:00:00|10:00:00|18:00:00
|2019-07-07|1|08:00:00|12:00:00|08:00:00|12:00:00
|2019-07-07|1|16:00:00|22:00:00|16:00:00|22:00:00

Is this possible to achieve?

I would try something like this. I selected 15 min time limit that a shift should start

select 
  t.date as date, t.user_id, 
  s.start_time as schedule_start, 
  s.end_time as schedule_end, 
  t.start_time as actual_start, 
  t.end_time as actual_end 
from time t 
  left join schedule s on 
  t.user_id=s.user_id and t.date=s.date 
  and s.start_time  BETWEEN t.start_time - INTERVAL 15 MINUTE 
  AND  t.start_time + INTERVAL 15 MINUTE
 order by date,schedule_start;

Grouping would you do be add up time for every day and user day

You need a much more complicated query to distinguish the 2 shifts.
So you must execute 2 separate queries each for each shift and combine them with UNION:

select
  s.date, s.user_id,
  s.schedule_start, 
  s.schedule_end, 
  t.actual_start, 
  t.actual_end   
from (
  select s.date, s.user_id, 
    min(s.start_time) as schedule_start, 
    min(s.end_time) as schedule_end
  from schedule s
  group by s.date, s.user_id
) s left join (
  select t.date, t.user_id,
    min(t.start_time) as actual_start, 
    min(t.end_time) as actual_end
  from time t
  group by t.date, t.user_id
) t on t.user_id=s.user_id and t.date=s.date 
union
select
  s.date, s.user_id,
  s.schedule_start, 
  s.schedule_end, 
  t.actual_start, 
  t.actual_end   
from (
  select s.date, s.user_id, 
    max(s.start_time) as schedule_start, 
    max(s.end_time) as schedule_end
  from schedule s
  group by s.date, s.user_id
) s left join (
  select t.date, t.user_id,
    max(t.start_time) as actual_start, 
    max(t.end_time) as actual_end
  from time t 
  group by t.date, t.user_id
) t on t.user_id=s.user_id and t.date=s.date

See the demo .
Results:

> date       | user_id | schedule_start | schedule_end | actual_start | actual_end
> :--------- | ------: | :------------- | :----------- | :----------- | :---------
> 2019-07-06 |       1 | 10:00:00       | 18:00:00     | 10:00:00     | 18:00:00  
> 2019-07-07 |       1 | 08:00:00       | 12:00:00     | 08:00:00     | 12:00:00  
> 2019-07-07 |       1 | 16:00:00       | 22:00:00     | 16:00:00     | 22:00:00  

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM