在用户Feed中对序列帖子进行分组

时间:2012-05-10 21:48:45

标签: mysql database-design grouping feed

我有图片帖子的用户Feed。每个用户都可以发布单个图像,但是,他可以经常重复动作。比如说,在一小时内上传几张图片。

如何有效地设计数据库表,以便用户在一小时内发布多个图像(逐个) - 我可以轻松地将这些串行帖子组合在一起,更多地插入INSERT或SELECT?


不建议使用多上传表格。事实并非如此:我刚才用更常见的术语描述了这个任务:)

4 个答案:

答案 0 :(得分:3)

您可以为每个帖子存储时间戳,然后从下一个项目中选择时间戳小于某个阈值的每个项目吗?

另一个想法是在每个帖子中存储时间戳和“组号”。在存储帖子之前,请执行SELECT以查找在过去n分钟内提交的帖子。如果找到一个,请为新帖子使用相同的组号。如果不这样做,则增加新帖子的组号。然后,您可以按组编号选择以查找所需的项目。

答案 1 :(得分:2)

我认为数据模型看起来很相似:

enter image description here

请注意确保帖子之间的时间差异大于TIMESTAMP的分辨率(或准备好优雅地处理PK违规)。

在支持分析功能的DBMS中,您可以相当轻松地对临时靠近的帖子进行分组。例如,对于彼此相隔一小时的帖子(对于给定用户)进行分组的Oracle查询将如下所示:

SELECT T.*, SUM(DIFF) OVER (ORDER BY TIMESTAMP) GROUPING
FROM (
    SELECT
        IMAGE.*,
        CASE
            WHEN TIMESTAMP <= LAG(TIMESTAMP) OVER (ORDER BY TIMESTAMP)
                + INTERVAL '1' HOUR
            THEN 0
            ELSE 1
            END DIFF
    FROM IMAGE
    WHERE USER_ID = :user_id
) T;

生成的GROUPING字段将标识TIMESTAMP“足够接近”的各个行组。这个查询也非常有效 - 它只是对PK索引的范围扫描。您可以在SQL Fiddle

中使用它

不幸的是,MySQL不支持分析功能,但在应用程序级别上执行本质上相同应该没有问题。只需SELECT ... ORDER BY TIMESTAMP,线性遍历结果,看看当前行和上一行之间的差异。

答案 2 :(得分:2)

那就是游乐场:

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;

每一行形成一个组ID,其组ID与行id相匹配(也可以是时间戳):

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`-@granularity 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`-@granularity 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`+@granularity
    )
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`-@granularity 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

答案 3 :(得分:1)

“o_O Tync”解决方案不会在1小时内对项目进行分组,例如:1:00,1:40,2:30。只会将最后两个分组。

这是超高速的Mysql解决方案,没有临时表和连接(同一个表)。

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;
UPDATE feed f CROSS JOIN (
  SELECT
    g.id,
    @id:=COALESCE( IF( ISNULL(@prev_date) OR (user_id!=@prev_user_id) OR NOT(@prev_date-tm BETWEEN 0 AND @granularity), g.id, NULL), @id)
    +least(0, @prev_date:=tm)
    +least(0, @prev_user_id:=user_id) as group_id    
  FROM (SELECT @prev_date:=null, @id:=null, @user_id:=null) r, feed g
  ORDER BY user_id DESC, tm DESC
) z USING (id)
SET f.group = z.group_id;

http://sqlfiddle.com/#!2/02a98/1/0