web-dev-qa-db-ja.com

スレッドメッセージングシステムデータベーススキーマの設計

私はここで説明されていることを正確に達成しようとしています: facebookやgmailのようなスレッド化されたプライベートメッセージングシステムの作成 ですが、Joel Brownの答えを完全には理解していません。どなたかご説明いただけますか?.

これは私のdbテーブルがサンプルデータでどのように見えるかです(私はデモの目的で正しく記入したと思います): enter image description here

  1. LoginId(最新が一番上)に基づいてスレッドのリストを表示する必要があります。LINQではクエリはどのように見えますか? (私が求めているのは、メッセージスレッドのグループです。各スレッドで最新のメッセージを1つ教えてください)-これはFacebookで行われるのと同じです。

  2. メッセージスレッドにすべてのメッセージを表示する必要があります(LINQ)-> Facebookでメッセージをクリックすると、「会話」全体がトレッドに表示されるのと同じです。

助けてください!ありがとう

[〜#〜] edit [〜#〜]->継続Joel、これは正しいですか?

enter image description here

ジョエル、少し混乱しています。説明してください(コメント/質問は太字):

ここでの考え方は、ユーザーが新しいスレッド/メッセージを開始するたびに、THREADテーブルの新しいレコードで開始するというものです。次に、ユーザーがTHREAD_PARTICIPANTとして追加され、メッセージのコンテンツがMESSAGEに追加されます。 MESSAGEからUSERへのFKは、メッセージの作成者を示します。

LoginId 1はLoginId2にメッセージを送信します=> MessageThreadテーブルに新しいレコードが挿入されます。また、MessageThreadId = 1、LoginId = 1(送信者)のレコードがMessageThreadParticipantレコードに挿入されます。そして、新しいレコードがMessageId = 1、MessageThreadid = 1、SenderLoginId = 1でメッセージテーブルに挿入されます(正しい??)

これはその繰り返しの後に私が持っているものです: enter image description here

Loginid 2が彼へのメッセージがあることを知る方法がないので、私は混乱していると思います。 ?? OR MessageThreadParticipantに2つのレコードを挿入する必要があるかもしれませんか??(送信者と受信者)->このようにして、両方が「会話」全体を見ることができますか?

EDIT2:ジョー、私はこれができると思います:

SELECT
  Message.MessageId, Message.CreateDate, Message.Body, Login.Username, Message.SenderLoginId
, (SELECT MessageReadState.ReadDate 
   FROM MessageReadState 
   WHERE MessageReadState.MessageId = Message.MessageId 
     ) as ReadDate
FROM Message 
    INNER JOIN Login ON Message.SenderLoginId = Login.LoginId
    INNER JOIN MessageThreadParticipant mtp on mtp.MessageThreadId = Message.MessageThreadId 
AND ( Message.MessageId in 
        ( SELECT Max(Message.MessageId)
          FROM MessageThreadParticipant INNER JOIN Message 
            ON MessageThreadParticipant.MessageThreadId = Message.MessageThreadId
          GROUP BY MessageThreadParticipant.MessageThreadId
        )
      )
Where mtp.LoginId = 2
ORDER BY Message.CreateDate DESC;

私が間違っている場合は修正してください:)

40
ShaneKm

さて、なぜあなたは尋ねないのですか? :)

あなたの要件に対する私の理解を突き止めましょう。あなたは2人の間のメッセージの線形リスト(ツリーではない)であるスレッドを見ているようです。たった2人ではなく、もっと多くの人を許可したいと思うかもしれません。それは、誰かがメッセージを投稿し、それから何人でもそれを読んで、コメントを追加し始める限り、Facebookのようなものです。コメントを追加すると、スレッドに移動し、ステータスの更新や、スレッドでのアクティビティなどを通知する電子メールを受信し始めます。それがあなたが望んでいるものであると仮定すると、私が提案したスキーマ Big Mike は、まさにあなたが探しているものではありません。

代わりに、以下を検討してください。

Schema

ここでの考え方は、ユーザーが新しいスレッド/メッセージを開始するたびに、THREADテーブルの新しいレコードで開始するというものです。次に、ユーザーがTHREAD_PARTICIPANTとして追加され、メッセージのコンテンツがMESSAGEに追加されます。 MESSAGEからUSERへのFKは、メッセージの作成者を示します。

ユーザーがメッセージを読むと、MESSAGE_READ_STATEテーブルにエントリが表示され、要件の状況に応じて、明示的または暗黙的にメッセージに既読のマークが付けられたことを示します。

スレッドの最初のメッセージに誰かがコメントすると、2番目のMESSAGEがFKで元のTHREADに追加され、返信の作成者(ユーザー)がTHREAD_PARTICIPANTテーブルに追加されます。そして、メッセージがスレッドに1人、2人、またはそれ以上の参加者によって追加されると、それが進みます。

任意のスレッドで最新のメッセージを取得するには、メッセージFKが対象のスレッドに送信される作成日(またはIDキー)で降順にソートされたMESSAGEの上位1を取得します。

ユーザーの最新の更新済みスレッドを取得するには、ユーザーがTHREAD_PARTICIPANTであるスレッドにメッセージが存在する作成日に降順にソートされたメッセージから、上位1に関連するTHREADを取得します。

LinqPadを壊さない限り、LINQでこれらのことを述べることはできないと思います。上記からの私のドリフトを捉えるのに問題がある場合は、テーブル定義といくつかのSQLで答えを具体化できます。コメント欄で質問してください。

編集:要件と実装の明確化

要件の明確化:当初、私はコメントを投稿する機会がある公開メッセージについて考えていましたが、シェーンはダイレクトメッセージ機能をさらに追求しています。その場合、最初の受信者を最初にTHREAD_PARTICIPANTテーブルに含める必要があります。

わかりやすくするために、いくつかの行をテーブルに入れましょう。以下はシナリオです(カナダデーに敬意を表して):ユーザー1がビールの打ち合わせについて質問するDMをユーザー2がDM。ユーザー2は、待ち合わせ場所についての質問に答え、ユーザー1は答えます。テーブルは次のようになります:(おそらく過度に単純化されています)

Sample Data Part 1Sample Data Part 2

EDIT#2:SQLにアクセスして、スレッド内のすべてのメッセージのリストを読み取り状態で...

@OPのスキーマを使用して、このSQLは、特定のユーザーが各メッセージを読んだかどうかを示す、特定のスレッド内のメッセージのリストを取得します。メッセージは最新の最初の順序です。

SELECT 
  Message.MessageId
, Message.CreateDate
, Message.Body
, Login.Username
, (SELECT MessageReadState.ReadDate 
   FROM MessageReadState 
   WHERE MessageReadState.MessageId = Message.MessageId 
     and MessageReadState.LoginId = 2) as ReadState
FROM (Message INNER JOIN Login ON Message.SenderLoginId = Login.LoginId) 
WHERE (((Message.MessageThreadId)=10))
ORDER BY Message.CreateDate DESC;

トリックは、それを呼び出すのが公正である場合、読み取り状態が副選択で取得されることです。読み取り状態を取得するための基準の一部には、外部結合では満足できないwhere句が必要であるため、これは必要です。したがって、副選択を使用して、MessageReadState子テーブルから必要な(欠落している可能性がある)値を特定します。

編集3:特定のユーザーの最新のメッセージを含むすべてのスレッドを取得するためのSQL ...

特定のユーザーが参加したすべてのスレッドのリストを取得するには、最初に最新のメッセージでソートし、最新のメッセージのみ(スレッドごとに1つのメッセージ)を表示してから、上記と同様のクエリを使用します。対象のスレッドへのFKでメッセージをフィルタリングする代わりに、対象のユーザーが参加した各スレッドで最新のメッセージを見つけるサブクエリでメッセージをフィルタリングします。次のようになります。

SELECT
  Message.MessageId
, Message.CreateDate
, Message.Body
, Login.Username
, (SELECT MessageReadState.ReadDate 
   FROM MessageReadState 
   WHERE MessageReadState.MessageId = Message.MessageId 
     and MessageReadState.LoginId = 2) AS ReadState
FROM Message INNER JOIN Login ON Message.SenderLoginId = Login.LoginId
WHERE ( Message.MessageId in 
        ( SELECT Max(Message.MessageId)
          FROM MessageThreadParticipant INNER JOIN Message 
            ON MessageThreadParticipant.MessageThreadId = Message.MessageThreadId
          WHERE MessageThreadParticipant.LoginId=2
          GROUP BY MessageThreadParticipant.MessageThreadId
        )
      )
ORDER BY Message.CreateDate DESC;
77
Joel Brown

Joel Brown'answerによると、LAST_MESSAGE_ID列をTHREADテーブルに挿入すると、最後のメッセージSQLを含むすべてのスレッドを取得することが非常に簡単になります。すべてのメッセージを送信するときに、この列を更新する必要があります。

特定のユーザーの最新のメッセージを含むすべてのスレッドを取得する

SELECT *
FROM THREAD T
INNER JOIN MESSAGE M ON T.LAST_MESSAGE_ID=M.MESSAGE_ID
INNER JOIN USER SENDER ON M.USER_ID=SENDER.USER_ID
LEFT JOIN MessageReadState MRS ON M.MESSAGE_ID=MRS.MESSAGE_ID AND MRS.USER_ID=2
2
Alexander