我正在实现类似于Twitter的Web应用程序。我需要实施“转发”操作,一条推文可以多次转发一个人。
我有一个基本的'推文'表,其中包含以下列:
推文: tweet_id | tweet_text | tweet_date_created | tweet_user_id
(其中tweet_id
是推文的主键,tweet_text
包含推文文本,tweet_date_created
是创建推文时的DateTime,tweet_user_id
是users
的外键1}}表并标识创建推文的用户)
现在我想知道如何在我的数据库中实现转推行动。
我应该创建新的连接表,如下所示:
转推:tweet_id | user_id | retweet_date_retweeted
(其中tweet_id
是tweets
表的外键,user_id
是users
表的外键,用于标识转发推文的用户,{{1是一个DateTime,它指定转推完成的时间。)
专业人员:当用户处理reteet时,将不会有空列,将创建retweet_date_retweeted
表中的新行。
缺点:查询过程会比较困难,需要加入两个表,然后以两个日期对推文进行排序(当推文不转发时,通过tweet_date_created对其进行排序,当推文转发时) ,按retweet_date_retweeted排序。
或者我应该在retweets
表格中tweets
实现它,它将如下所示:
推文: tweet_id | tweet_text | tweet_date_created | tweet_user_id | PARENT_ID
(其中所有列保持不变,parent_id
是同一parent_id
表的外键。创建推文时,tweets
仍然为空。当转发推文时,{ {1}}包含原始推文ID,parent_id
包含处理转发操作的用户,parent_id
包含转发完成时的DateTime,tweet_user_id
仍然为空 - 因为我们将不允许用户在转发时更改原始推文。)
专业人士:查询过程要优雅得多,因为我不必加入两个表。
缺点:每次转发推文都会有空单元格。因此,如果我的数据库中有1 000条推文,并且每条推文都被转发了5次,那么我的tweet_date_created
表格中就会有5 000行。
哪种方式最有效?拥有空单元格或查询过程更干净更好吗?
答案 0 :(得分:10)
IMO选项#1会更好。加入tweet和转推表的查询并不复杂,可以通过左连接或内连接来完成,具体取决于您是要显示所有推文还是仅显示转发的推文。并且连接查询应该是高性能的,因为表是窄的,被连接的列是整数,并且由于FK约束它们将各自具有索引。
另一个建议是不要用tweet或转发标记所有列,这些可以从存储数据的表中推断出来,例如:
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 |
+----+----------------+---------------------+
答案 1 :(得分:1)
我会稍微修改选择选项2。如果Twitter不是转发,则推文表中的列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执行得更好,因为只使用一个物理列进行排序,可以将其编入索引。
唯一的缺点是数据库会有点大。