This is a playground:
CREATE TABLE `feed`( `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, `tm` INT UNSIGNED NOT NULL COMMENT 'timestamp', `user_id` INT UNSIGNED NOT NULL COMMENT 'author id', `image` VARCHAR(255) NOT NULL COMMENT 'posted image filename', `group` INT UNSIGNED NULL DEFAULT NULL COMMENT 'post group', PRIMARY KEY(`id`), INDEX(`user_id`), INDEX(`tm`,`group`) );
We would like to group messages that are temporarily closed.
First declare the desired granularity: threshold for temporal proximity:
SET @granularity:=60*60;
Each line forms a group with a group identifier corresponding to a line identifier (it can also be a timestamp):
SELECT `g`.`id` AS `group` FROM `feed` `g`;
Each group contains lines that come from the same user, were published earlier than the former group:
SELECT `g`.`id` AS `group`, `f`.* FROM `feed` `g` CROSS JOIN `feed` `f` ON (`f`.`user_id` = `g`.`user_id` AND `f`.`tm` BETWEEN `g`.`tm` -@granularity AND `g`.`tm` )
Each row belongs to several groups. For each row, we select the "widest" group: it has the largest rowId
SELECT MAX(`g`.`id`) AS `group`, `f`.* FROM `feed` `g` CROSS JOIN `feed` `f` ON (`f`.`user_id` = `g`.`user_id` AND `f`.`tm` BETWEEN `g`.`tm` -@granularity AND `g`.`tm` ) GROUP BY `f`.`id`
The last updated group always jumps up (if you sort by group
DESC). However, if you want the groups to be constant (for example, so that the elements do not move from one group to another), use MIN
instead of MAX
:
SELECT MIN(`g`.`id`) AS `group`, `f`.* FROM `feed` `g` CROSS JOIN `feed` `f` ON (`f`.`user_id` = `g`.`user_id` AND `f`.`tm` BETWEEN `g`.`tm` AND `g`.`tm` +@granularity ) GROUP BY `f`.`id`
Now we are going to update the group
column. First, MySQL cannot update the same table you are reading from. We need a temporary table. Second: we update rows whose group
column is NULL, or rows placed later by UNIX_TIMESTAMP()-2*@threshold
:
CREATE TEMPORARY TABLE `_feedg` SELECT MAX(`g`.`id`) AS `group`, `f`.`id` FROM `feed` `g` CROSS JOIN `feed` `f` ON (`f`.`user_id` = `g`.`user_id` AND `f`.`tm` BETWEEN `g`.`tm` -@granularity AND `g`.`tm` ) WHERE `f`.`group` IS NULL OR `f`.`tm` >= (UNIX_TIMESTAMP()-2*@granularity) GROUP BY `f`.`id`;
And update the group
column:
UPDATE `feed` `f` CROSS JOIN `_feedg` `g` USING(`id`) SET `f`.`group` = `g`.`group`;
Here's SQLFiddle: http://sqlfiddle.com/#!2/be9ce/15