8

我正在实现类似于 Twitter 的 Web 应用程序。我需要实现“转发”操作,一条推文可以被一个人转发多次

我有一个基本的“推文”表,其中包含以下列:

推文: tweet_id | 推文文本 | tweet_date_created | tweet_user_id

(其中tweet_id是推文的主键,tweet_text包含推文文本,tweet_date_created是创建推文时的日期时间,tweet_user_id是表的外键users并标识创建推文的用户)

现在我想知道我应该如何在我的数据库中实现转发操作。

选项1

我是否应该创建新的连接表,如下所示:

转推: tweet_id | 用户 ID | 转推日期转推

(哪里tweet_id是表的外键tweetsuser_id是表的外键users并标识已转发推文的用户,retweet_date_retweeted是指定转发完成时间的日期时间。)

优点:不会有空列,当用户进程重新发送时,retweets将在表中创建新行。

缺点:查询过程会比较困难,它需要连接两个表,并以某种方式按两个日期对推文进行排序(当推文不转发时,按 tweet_date_created 排序,当推文被转发时,按 retweet_date_retweeted 排序)。

选项 2

或者我应该在tweets表中实现它parent_id,它看起来像这样:

推文: tweet_id | 推文文本 | tweet_date_created | tweet_user_id | parent_id

(所有列保持不变,并且parent_id是同一个表的外键tweets。创建推文时,parent_id保持为空。当推文被转发时,parent_id包含原始推文 ID,tweet_user_id包含处理转发操作的用户,tweet_date_created包含转发时的 DateTime完成,并且tweet_text保持空白 -因为我们不会让用户在转发时更改原始推文。)

优点:查询过程更加优雅,因为我不必连接两个表。

缺点:每次转发推文时都会有空单元格。因此,如果我的数据库中有 1 000 条推文,并且每条推文都被转发 5 次,那么我的tweets表中将有 5 000 行。


哪种方式最有效?有空单元格更好还是让查询过程更干净?

4

2 回答 2

9

IMO 选项#1 会更好。加入推文和转推表的查询一点也不复杂,可以通过左连接或内连接来完成,具体取决于您是要显示所有推文还是只显示被转推的推文。并且连接查询应该是高性能的,因为表很窄,被连接的列是整数,并且由于 FK 约束,它们每个都有索引。

另一个建议是不要用 tweet 或 retweet 标记所有列,这些可以从存储数据的表中推断出来,例如:

tweet
    id
    user_id
    text
    created_at

retweet
    tweet_id
    user_id
    created_at

样本加入:

# Return all tweets which have been retweeted
SELECT
    count(*),
    t.id
FROM
    tweet AS t
INNER JOIN retweet AS rt ON rt.tweet_id = t.id
GROUP BY
    t.id

# Return tweet and possible retweet data for a specific tweet
SELECT
    t.id
FROM
    tweet AS t
LEFT OUTER JOIN retweet AS rt ON rt.tweet_id = t.id
WHERE
    t.id = :tweetId

-- 根据请求更新 --

以下只是说明性的,代表我为什么选择选项#1,没有外键也没有任何索引,您必须自己添加这些。但结果应该表明连接不会太痛苦。

CREATE TABLE `tweet` (
    `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
    `user_id` int(10) unsigned NOT NULL,
    `value` varchar(255) NOT NULL,
    `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=8 DEFAULT CHARSET=utf8

CREATE TABLE `retweet` (
    `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
    `tweet_id` int(10) unsigned NOT NULL,
    `user_id` int(10) unsigned NOT NULL,
    `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

# Sample Rows

mysql> select * from tweet;
+----+---------+----------------+---------------------+
| id | user_id | value          | created_at          |
+----+---------+----------------+---------------------+
|  1 |       1 | User1 | Tweet1 | 2012-07-27 00:04:30 |
|  2 |       1 | User1 | Tweet2 | 2012-07-27 00:04:35 |
|  3 |       2 | User2 | Tweet1 | 2012-07-27 00:04:47 |
|  4 |       3 | User3 | Tweet1 | 2012-07-27 00:04:58 |
|  5 |       1 | User1 | Tweet3 | 2012-07-27 00:06:47 |
|  6 |       1 | User1 | Tweet4 | 2012-07-27 00:06:50 |
|  7 |       1 | User1 | Tweet5 | 2012-07-27 00:06:54 |
+----+---------+----------------+---------------------+

mysql> select * from retweet;
+----+----------+---------+---------------------+
| id | tweet_id | user_id | created_at          |
+----+----------+---------+---------------------+
|  1 |        4 |       1 | 2012-07-27 00:06:37 |
|  2 |        3 |       1 | 2012-07-27 00:07:11 |
+----+----------+---------+---------------------+

# Query to pull all tweets for user_id = 1, including retweets and order from newest to oldest

select * from (
    select t.* from tweet as t where user_id = 1
    union
    select t.* from tweet as t where t.id in (select tweet_id from retweet where user_id = 1))
a order by created_at desc;

mysql> select * from (select t.* from tweet as t where user_id = 1 union select t.* from tweet as t where t.id in (select tweet_id from retweet where user_id = 1)) a order by created_at desc;
+----+---------+----------------+---------------------+
| id | user_id | value          | created_at          |
+----+---------+----------------+---------------------+
|  7 |       1 | User1 | Tweet5 | 2012-07-27 00:06:54 |
|  6 |       1 | User1 | Tweet4 | 2012-07-27 00:06:50 |
|  5 |       1 | User1 | Tweet3 | 2012-07-27 00:06:47 |
|  4 |       3 | User3 | Tweet1 | 2012-07-27 00:04:58 |
|  3 |       2 | User2 | Tweet1 | 2012-07-27 00:04:47 |
|  2 |       1 | User1 | Tweet2 | 2012-07-27 00:04:35 |
|  1 |       1 | User1 | Tweet1 | 2012-07-27 00:04:30 |
+----+---------+----------------+---------------------+

请注意,在最后一组结果中,我们还能够包含转推并在 #3 转推之前显示 #4 的转推。

- 更新 -

您可以通过稍微更改查询来完成您的要求:

select * from (
    select t.id, t.value, t.created_at from tweet as t where user_id = 1
    union
    select t.id, t.value, rt.created_at from tweet as t inner join retweet as rt on rt.tweet_id = t.id where rt.user_id = 1)
a order by created_at desc;

mysql> select * from (select t.id, t.value, t.created_at from tweet as t where user_id = 1 union select t.id, t.value, rt.created_at from tweet as t inner join retweet as rt on rt.tweet_id = t.id where rt.user_id = 1) a order by created_at desc;
+----+----------------+---------------------+
| id | value          | created_at          |
+----+----------------+---------------------+
|  3 | User2 | Tweet1 | 2012-07-27 00:07:11 |
|  7 | User1 | Tweet5 | 2012-07-27 00:06:54 |
|  6 | User1 | Tweet4 | 2012-07-27 00:06:50 |
|  5 | User1 | Tweet3 | 2012-07-27 00:06:47 |
|  4 | User3 | Tweet1 | 2012-07-27 00:06:37 |
|  2 | User1 | Tweet2 | 2012-07-27 00:04:35 |
|  1 | User1 | Tweet1 | 2012-07-27 00:04:30 |
+----+----------------+---------------------+
于 2012-07-25T17:17:14.073 回答
1

我会选择选项 2,稍作修改。parent_id如果不是转推,则推文表中的列应指向自身。然后,查询将非常容易:

SELECT tm.Id, tm.UserId, tc.Text, tm.Created, 
    CASE WHEN tm.Id <> tc .Id THEN tm.UserId ELSE NULL END AS OriginalAsker
FROM tweet tm
LEFT JOIN tweet tc ON tm.ParentId = tc.Id
ORDER BY tm.Created DESC

tc是父表 - 有内容的表。它有推文的文本、原始海报的 ID 等)

引入如果不转发则指向自身的规则的原因是,很容易向原始推文添加更多连接。您只需加入一个表格,tc而不关心它是否转发。

不仅查询很简单,而且它的性能也比选项 1好得多,因为排序只使用一个可以索引的物理列完成。

唯一的缺点是数据库会大一点。

于 2012-07-29T23:49:24.370 回答