Reputation: 3
I have a table like:
user | area | start | end
1 1 12 18
1 1 19 27
1 1 29 55
1 1 80 99
means: a 'user' appeared in an 'area' from time 'start' to time 'end', areas can be overlapped.
what I want is to get a result like:
user | start-end
1 12-18,19-27,29-55
1 80-99
which means: combine appears with time difference less than a specified value, i.e (row2.start - row1.end < 10), and one result row stands for one 'visit' of the area for a user. Currently I can distinguish each visit and get the count of visits by comparing the same table using one sql statement. But I'm not able to find a way to get the above result. Any help is appreciated.
Explanation: The first 3 appears are linked together as only one visit because: row2.start-row1.end < 10 and row3.start-row2.end < 10, the last appear is a new visit because:80(row4.start) - 55(row3.end) >= 10 .
Upvotes: 0
Views: 155
Reputation: 65264
We need two steps:
1 - combine a row with its predcessor to have start and last end in the same row
SELECT
user, area, start, end, @lastend AS lastend, @lastend:=end AS ignoreme
FROM
tablename,
(SELECT @lastend:=0) AS init
ORDER BY user, area, start, end;
2 - use the difference as a grouping criterion
SELECT
...
FROM
...
(SELECT @groupnum:=0) AS groupinit
GROUP BY
... ,
IF(start-lastend>=10,@groupnum:=@groupnum+1,@groupnum)
Now let's combine it:
SELECT
user, area,
GROUP_CONCAT(CONCAT(start,"-",end)) AS start_end
FROM (
SELECT
user, area, start, end, @lastend AS lastend, @lastend:=end AS ignoreme
FROM
tablename,
(SELECT @lastend:=0) AS init
ORDER BY user, area, start, end
) AS baseview,
(SELECT @groupnum:=0) AS groupinit
GROUP BY
user, area,
IF(start-lastend>=10,@groupnum:=@groupnum+1,@groupnum)
Edit
Fixed typos and verified: SQLfiddle
Upvotes: 1