Reputation: 817
I have a question. Suppose I have this table in SQL:
date user_id
2015-03-17 00:06:12 143
2015-03-17 01:06:12 143
2015-03-17 02:06:12 143
2015-03-17 09:06:12 143
2015-03-17 10:10:10 200
I want to get the number of consecutive hours. For example, for user 143, I want to get 2 hours, for user 200 0 hours. I tried like this :
select user_id, TIMESTAMPDIFF(HOUR,min(date), max(date)) as hours
from myTable
group by user_id
But this query fetches all non-consecutive hours. Is it possible to solve the problem with a query, or do I need to post-process the results in PHP?
Upvotes: 2
Views: 264
Reputation: 21492
Another version using temporary variables:
SET @u := 0;
SET @pt := 0;
SET @s := 0;
SELECT `user_id`, MAX(`s`) `conseq` FROM
(
SELECT
@s := IF(@u = `user_id`,
IF(UNIX_TIMESTAMP(`date`) - @pt = 3600, @s + 1, @s),
0) s,
@u := `user_id` `user_id`,
@pt := UNIX_TIMESTAMP(`date`) pt
FROM `users`
ORDER BY `date`
) AS t
GROUP BY `user_id`
The subquery sorts the rows by date, then compares user_id
with the previous value. If user IDs are equal, calculates the difference between date
and the previous timestamp @pt
. If the difference is an hour (3600 seconds), then the @s
counter is incremented by one. Otherwise, the counter is reset to 0:
s user_id pt
0 143 1426529172
1 143 1426532772
2 143 1426536372
2 143 1426561572
0 200 1426565410
The outer query collects the maximum counter values per user_id
, since the maximum counter value corresponds to the last counter value per user_id
.
Output
user_id conseq
143 2
200 0
Note, the query accepts the difference of exactly 1 hour. If you want a more flexible condition, simply adjust the comparison. For example, you can accept a difference in interval between 3000 and 4000 seconds as follows:
@s := IF(@u = `user_id`,
IF( (UNIX_TIMESTAMP(`date`) - @pt) BETWEEN 3000 AND 4000, @s + 1, @s),
0) s
Upvotes: 1
Reputation: 51868
Use a variable to compare with the previous row.
SELECT user_id, SUM(cont_hour) FROM (
SELECT
user_id,
IF(CONCAT(DATE(@prev_date), ' ', HOUR(@prev_date), ':00:00') - INTERVAL 1 HOUR = CONCAT(DATE(t.date), ' ', HOUR(t.date), ':00:00')
AND @prev_user = t.user_id, 1, 0) AS cont_hour
, @prev_date := t.date
, @prev_user := t.user_id
FROM
table t
, (SELECT @prev_date := NULL, @prev_user := NULL) var_init_subquery
WHERE t.date BETWEEN <this> AND <that>
ORDER BY t.date
) sq
GROUP BY user_id;
I made the comparison a bit more complicated than you expected, but I thought it's necessary, that you don't just compare the hour, but also, that it's the same date (or the previous day, when it's around midnight).
As a short explanation: The ORDER BY
is very important, as well as the order in the SELECT
clause. The @prev_date
holds the "previous row", cause we assign the value of the current row after we made our comparison.
Upvotes: 2