8

Twitterに似たWebアプリケーションを実装しています。「リツイート」アクションを実装する必要があります。1つのツイートは、1人のユーザーが複数回リツイートすることで実行できます。

次の列を持つ基本的な「ツイート」テーブルがあります。

ツイート: tweet_id | tweet_text | tweet_date_created | tweet_user_id

(ここでtweet_id、はツイートの主キーであり、tweet_textツイートテキストを含み、tweet_date_createdはツイートが作成された日時であり、テーブルtweet_user_idへの外部キーでありusers、ツイートを作成したユーザーを識別します)

今、データベースにリツイートアクションをどのように実装すればよいのか疑問に思っています。

オプション1

次のような新しい結合テーブルを作成する必要があります。

リツイート:tweet_id | user_id | retweet_date_retweeted

(ここtweet_idで、はテーブルへの外部キー、はtweetsテーブルへの外部キーであり、ツイートをリツイートしたユーザーを識別し、はリツイートがいつ行われたかを指定するDateTimeです。)user_idusersretweet_date_retweeted

長所:空の列はありません。ユーザーがreteetを処理すると、retweetsテーブルに新しい行が作成されます。

短所:クエリプロセスはより難しくなります。2つのテーブルを結合し、ツイートを2つの日付で並べ替える必要があります(ツイートがリツイートされていない場合はtweet_date_createdで並べ替え、ツイートがリツイートされている場合はretweet_date_retweetedで並べ替えます)。

オプション2

tweetsまたは、テーブルに次のように実装すると、次のparent_idようになります。

ツイート: tweet_id | tweet_text | tweet_date_created | tweet_user_id | parent_id

(すべての列が同じであり、同じテーブルparent_idへの外部キーである場合。ツイートが作成されると、空のままになります。ツイートがリツイートされると、元のツイートIDが含まれ、リツイートアクションを処理したユーザーが含まれ、リツイートが行われたときの日時が含まれます。完了し、空のままになります-リツイート時にユーザーが元のツイートを変更できないようにするためです。)tweetsparent_idparent_idtweet_user_idtweet_date_createdtweet_text

長所: 2つのテーブルを結合する必要がないため、クエリプロセスははるかに洗練されています。

短所:ツイートがリツイートされるたびに空のセルがあります。したがって、データベースに1,000のツイートがあり、それらすべてが5回リツイートされた場合、tweetsテーブルには5000行になります。


最も効率的な方法はどれですか?空のセルを使用するか、クエリプロセスをよりクリーンにする方がよいでしょうか。

4

2 に答える 2

9

IMOオプション#1の方が良いでしょう。ツイートテーブルとリツイートテーブルを結合するクエリは、まったく複雑ではなく、すべてのツイートを表示するか、リツイートされたツイートのみを表示するかに応じて、左結合または内部結合を介して実行できます。また、テーブルが狭く、結合される列がintであり、FKの制約によりそれぞれにインデックスがあるため、結合クエリはパフォーマンスが高い必要があります。

もう1つの推奨事項は、すべての列にツイートまたはリツイートのラベルを付けないことです。これらは、データが格納されているテーブルから推測できます。次に例を示します。

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つの物理列のみを使用して並べ替えが行われるため、オプション1よりもはるかに優れたパフォーマンスを発揮します。

唯一の欠点は、DBが少し大きくなることです。

于 2012-07-29T23:49:24.370 に答える