我有图片帖子的用户Feed。每个用户都可以发布单个图像,但是,他可以经常重复动作。比如说,在一小时内上传几张图片。
如何有效地设计数据库表,以便用户在一小时内发布多个图像(逐个) - 我可以轻松地将这些串行帖子组合在一起,更多地插入INSERT或SELECT?
不建议使用多上传表格。事实并非如此:我刚才用更常见的术语描述了这个任务:)
答案 0 :(得分:3)
您可以为每个帖子存储时间戳,然后从下一个项目中选择时间戳小于某个阈值的每个项目吗?
另一个想法是在每个帖子中存储时间戳和“组号”。在存储帖子之前,请执行SELECT
以查找在过去n
分钟内提交的帖子。如果找到一个,请为新帖子使用相同的组号。如果不这样做,则增加新帖子的组号。然后,您可以按组编号选择以查找所需的项目。
答案 1 :(得分:2)
我认为数据模型看起来很相似:
请注意确保帖子之间的时间差异大于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;