You can break it to two tables, something like this:
TblMessage ( Message_Id int identity(1,1) primary key, Message_SentDate datetime not null default(getDate()), Message_Title varchar(100), Message_Content varchar(max), Message_SenderId int, -- (fk to users) Message_IsDraft bit not null default(0), -- when 1 it's saved as draft. Message_IsDeletedFromOutbox bit not null default(0)-- when 1 don't show on sender outbox ) TblMessageToRecipient ( MTR_UserId int, -- (fk to users) MTR_Message_Id int, -- (fk to message) MTR_ReadDate datetime null, (if null then status is new) MTR_DeleteDate datetime null, (if not null then status is deleted) PRIMARY KEY (MTR_UserId, MTR_Message_Id) )
This way you can give the recipient an option to "delete forever" a message and just delete the relevant record from TblMessageToRecipient.
Also, you can delete the message completely from tblMessage if it doesn't have a reference on the TblMessageToRecipient and Message_DeletedFromOutbox = 1 (this can be done by a scheduled sql agent job to prevent tblMessages from getting too big)
update:
I hope this will answer your question in the comment:
The recipient has several possible statuses:
- New - when
MTR_ReadDate is null. - Read -when
MTR_ReadDate is not null, and MTR_DeleteDate is null. - Recycled - when
MTR_DeleteDate is null. - Deleted - when the record is deleted from
TblMessageToRecipient.
The sender have only 3 possible statuses:
Draft.
I've added a bit column to the TblMessage called IsDraft. note that drafts should also save recipient information, so it should be saved in both tables, just show it to the sender in the drafts box, and don't show it to the recipient. Note that when the sender discard the draft you should delete the message from both tables.
Sent.
Once the message is in both tables, and IsDraft = 0, and IsDeletedFromOutbox = 0, it means that the message was sent. in this case, show it to the sender in the sent messages box, and show it to the recipient.
Deleted from outbox.
When IsDeletedFromOutbox = 1 you simply don't show the message to the sender.
if the message record does not have any references in the TblMessageToRecipient, you can delete the record from TblMessages since it was deleted by the sender and by all of it's recipients.
Update 2:
To summerize our conversation in the comments, there are 2 mahor ways to keep a conversation structure (meaning the link between a message and a reply to it [and it's reply and so on...])
One way is to keep a Message_ParentId nullable column in TblMessages. This column will contain null for any message that is not a reply to an older message, but for replies it will contain the message id of the message it was a reply to.
The second way is to keep a Message_ConversationId column that will always contain a value. when a message is a reply to an older message, it's Message_ConversationId should be the same as it's parent message. When it's not a reply, it's ConversationId should be generated. Since we are talking about sql server 2008, it means the easiest way to generate a new conversation id every time will be to add a new table called TblConversation. this table can keep a single column Conversation_Id that will be an int identity column, and to get a new conversation id do something like this:
DECLARE @ConversationId int INSERT INTO TblConversation DEFAULT VALUES SELECT @ConversationId = SCOPE_IDENTITY()
and then user the @ConversationId when inserting a new root message.