Это вне площадки:
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`)
);
Мы хотели бы, чтобы сгруппировать сообщения, которые временно близко.
Во-первых, объявить желаемое зернистость: порог для временной близости:
SET @granularity:=60*60;
Каждая строка образует группу с идентификатором группы, соответствующим идентификатор строки (он также может быть метку времени):
SELECT `g`.`id` AS `group`
FROM `feed` `g`;
Каждая группа содержит строки, которые исходят из того же пользователя, были опубликованы ранее, чем группа формирующей:
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`[email protected] AND `g`.`tm`
)
Каждая строка принадлежит нескольким группам. Для каждой строки, мы выбираем наиболее «широкую» группу: она имеет самый большой 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`[email protected] AND `g`.`tm`
)
GROUP BY `f`.`id`
Наиболее недавно обновленная группа всегда подскакивает к вершине (если сортировать по group
DESC). Однако, если вы хотите группы быть постоянными (например, так предметы не переходить из одной группы в другую), используйте MIN
вместо 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`[email protected]
)
GROUP BY `f`.`id`
Теперь мы собираемся обновить таблицы group
колонка. Во-первых, MySQL не может обновить ту же таблицу, с которой вы читаете. Нам нужна временная таблица. Во-вторых, мы обновит только строки, в которых group
столбец NULL, или строки размещены позднее 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`[email protected] AND `g`.`tm`
)
WHERE `f`.`group` IS NULL
OR `f`.`tm` >= (UNIX_TIMESTAMP()-2*@granularity)
GROUP BY `f`.`id`;
и обновить group
колонки:
UPDATE `feed` `f` CROSS JOIN `_feedg` `g` USING(`id`)
SET `f`.`group` = `g`.`group`;
Вот SQLFiddle: http://sqlfiddle.com/#!2/be9ce/15
Любой столбец можно, в том числе временной метки. Но тогда, как выбирать, делая «близкие» ряды вместе? – kolypto
Обновлено. Когда вы пытаетесь решить такую проблему, забудьте о компьютере и базе данных; просто подумайте об этом логично: как вы можете «связать» несколько предметов вместе? –
Конечно! Однако я не уверен, что это самый эффективный вариант :) Если я использую ваше предложение, тогда мне придется использовать [эту технику] (http://stackoverflow.com/questions/10542647/grouping-serial-posts -in-a-user-feed), чтобы отобразить фид – kolypto