Ok i was told my last question was too wide so i try it more precise this time.
I need a Database Structure for a MessageBoard App.
I have 2 ideas but dont know which scales better.
Is there another possibility that is even better than my ideas?
There are Users, Messages and Groups.
All messages belong into at least one group but can be in more.
Users subsscribe to at least one Group and than can see all Messages of all Groups they belong to.
Users can create Groups at will.
Scale(theoretically):
Users: Millions
Messages: Billions
Groups: More than Users
I have two ideas right now:
Idea 1:
Table Users:
ID
All personal information...
GroupsSubscribed (string with all group IDs?)
LastUpdate (Date)
Table Messages:
ID
ImageURI
Text
Creator
Date
Groups
Answer_Messages_IDs (String with all IDs of messages that are an answer to this message)
Table Groups:
ID
GroupName
LastUpdate (Date)
Idea:
Message Get:
App gets every X seconds the Group-LastUpdate (DB call: Group)
If Group-LastUpdate > User-LastUpdate ->
Select all Messages where Groups contain Group and Date > LastUpdate (DB call: Messages)
Message Write:
App writes Message belonging to more Groups
Save Message in Message Table (DB call)
Update Group Table LastUpdate (DB call)
-----------------
Idea 2:
Table Users:
ID
All personal information...
GroupsSubscribed (string with all group IDs?)
NewMessages (string with MessageIDs?)
Table Messages:
ID
ImageURI
Text
Creator
Date
Groups
Answer_Messages_IDs (String with all IDs of messages that are an answer to this message)
Table Groups:
ID
GroupName
UserIDs (string with all user IDs)
Idea:
Message Get:
App gets every X seconds the User-NewMessages(DB call: Users)
If User-NewMessages != "" ->
Select all Messages where ID in List of NewMessages (DB call: Messages)
Message Write:
App writes Message belonging to more Groups
Save Message in Message Table (DB call: Messages)
Get Groups-UserIDs for every Group (DB call: Groups)
Update every User with new Message ID (DB call: Users)
This is an exercise in database normalization as #Paul Spiegel indicates above.
You would create something like the following:
Users
UserID PK
ImageURI
... personal user informational columns ...
Messages
MessageID PK
Text
UserID FK -> Users(UserID) // Message Author (Creator)
Date
Replies
MessageID FK -> Messages(MessageID)
ReplyID FK -> Messages(MessageID)
PK (MessageID, ReplyID)
Groups
GroupID PK
Name
Description
UserID FK -> Users(UserID) // Group Moderator -- I'm just adding this one in for fun.
User_Groups
UserID FK -> Users(UserID)
GroupID FK -> Groups(GroupID)
PK (UserID, GroupID)
Message_Groups
MessageID FK -> Messages(MessageID)
GroupID FK -> Groups(GroupID)
PK (MessageID, GroupID)
I moved ImageID from Messages to Users on the assumption that it is a user Avatar. If it is really something else associated with a message, then move it back.
There are three application integrity rules in addition to the PKs and FKs already included.
AIR #1 - The existence of a row in Messages implies at least one matching row in Message_Groups.
AIR #2 - The existence of a row in Users implies at least one matching row in User_Groups.
AIR #3 - A given ReplyID can only appear once in Replies. This keeps adjacency list semantics preventing a generalized many-to-many association and enforcing a hierarchical association.
The database join logic and application code is left as an exercise to the reader.
Related
I have created 3 tables: accounts, products, claims. These are the relationships:
Accounts: PK - username
Products: PK - serial number, FK - username
Claims: FK - username, FK - serial number
My issue is that a user can add a claim even for products the user has not purchased, as long as the user knows that serial number, while I should allow the user to add a claim only for products the user purchased. For inserting claims I am using this query:
INSERT INTO claims (username, serial_no, date, issue) VALUES (%s, %s, %s, %s)
Do I need to change the table relationship with keys to fix the above, or use a more specific query? Better, what is the logic behind how this is supposed to work?
You should filter the products by username of the current user to avoid claiming products that are not related to the user. If you filter the product to be claimed then you won't need to change your table structure, it's only up to you on how you'll implement the filter in the programming language you are using.
First select all the products related to the user.
SELECT * FROM products WHERE username=#currentusername;
Then in your program, just allow the user to claim only from the fetched products.
I'm creating an application where there are multiple groups that contain a feed of messages. I need to know which message is read for X person. The feed should also be customizable per user.
I was thinking about using the following database structure:
messages
- id (PK)
- user_id (user that created the message)
- text
- group_id
- created_at
objects
- id (PK)
- user_id (user that should receive the object in his timeline)
- parent_type (can be `message` or some other resource)
- parent_id (the `id` of the resource based on the type)
- seen (boolean that indicates if the user has seen the object)
When a message is created for a group, I'll create a record in the messages table and create records for every member of the group in the objects table.
The only thing that is currently holding me back is, that there might be groups that consist of ~1000-2000 users at max. For every message created in those groups, there will be created 1000-2000 records in the objects table.
Will this be a problem and how could I solve this if it might be?
I have not found a tutorial yet on the internet for what I am looking for.
Functionality I am after is a simple message send and retrieve function.
User 1 - Sends Message to User 2
User 2 can view messages in their Inbox
I know I will need 2 controllers one for Handling the message sending and the other to retrieve.
How would this type of system function on the DB side. Do i make a new Table for Inbox and so all messages are sent there with corresponding id of sender and receiver?
A simple 2 tables should be enough for this:
TblUsers (UserId, UserName, Password, FirstName, LastName)
TblMessages (FromUser [fk to tblUsers], ToUser [fk to tblUsers], Title, Content, SentDate, ReadDate, DeletedDate)
Note the DeletedDate is only relevant if you want to enable a recycle bin for messages.
If you want to enable sending the same messages to multiple users, you need a little different schema:
TblMessags(MessageId, FromUser [fk to tblUsers], Title, Content, SentDate)
TblRecipients(MessageId [fk to messages], RecipientId [fk to users], ReadDate, DeletedDate)
Since you require help with the table structure and not with the implementation itself, here is a possible scenario:
A table for all users
A table for all messages
Keep in mind that the users table and the messages table is up to you define the attributes you want to store in them. An initial approach could be:
CREATE TABLE users(userID int, username VARCHAR(30));
CREATE TABLE messages(messageID int, senderID int, recipientID int, title VARCHAR(50), content VARCHAR(1000));
Getting a specific user's Inbox could then be done using the following query:
SELECT *
FROM messages
WHERE messages.recipientID=XXXX
XXXX is of course the userID of the user's inbox you want. This would only get the message data. If you wanted to display the sender data you could join the result of the previous query with a query for senderID or recipientID.
Table structure can be changed according to what type of queries you're anticipating. I believe this should suffice as an initial approach, and then you can analyse whether you need to group or index data that gets requested a lot.
Do I make a new Table for Inbox and so all messages are sent there with corresponding id of sender and receiver?
Basically, yes.
I realize that you want to build a simple message table with one sender and one receiver. You might as well design the tables for one sender and multiple receivers, in case you want to expand your message system.
Let's make the user table.
User
----
User ID
User Name
...
And the message table.
Message
-------
Message ID
Message Sent Time Stamp
Message Text
A user can send more than one message and receive more than one message. So, we build a junction table.
MessageUser
-----------
Message ID
User ID
Sender / Receiver Flag
...
The primary key is (Message ID, User ID). You will also have a unique index on (User ID, Message ID). The primary key allows you to get a message to the user. The unique index allows you to build a list of previous messages for a user.
I've got 3 tables for a chat system:
conversation this table holds an id and timestamp
conversation_participant tells me which users are in the chat
conversation_message the messages send within this chat (also holds sender_id)
This setup works quite good for me but now I want to do a 'reverse' lookup. I know which users are going to start a new chat and I want to know if this specific group already has a conversation in the DB. Is there a way to find a row based on a dynamic set of foreign rows?
(preferable without pivot like tricks)
Or is my database design flawed and should I alter that?
CONVERSATION
id int auto_increment
start timestamp
CONVERSATION_PARTICIPANT
conversation_id int (foreign key to conversation)
participant_id int (foreign key to users table)
CONVERSATION_MESSAGE
id int auto_increment
conversation_id int (foreign key to conversation)
author_id int
time timestamp
message text
This assumes you:
have a list of comma delimited participant_id's -> $list
know the number of participants -> $qty
use one of the participants in the WHERE clause -> $participant
replace those pseudo-variable with real values
You can see the code here: http://sqlfiddle.com/#!2/e90f2/11
Code:
SELECT conversation_participant.conversation_id AS conversation_id
, SUM(IF(members.participant_id IN ($list),1,0)) AS member_count
, COUNT(*) AS total
FROM conversation_participant
JOIN conversation_participant AS members
ON members.conversation_id = conversation_participant.conversation_id
WHERE conversation_participant.participant_id = $participant
GROUP BY conversation_id
HAVING member_count = total
AND member_count = $qty;
FYI: The purpose of the WHERE clause is to limit the number of potential conversations.
I have a user_id colunm in the user table. Next I have a brand_id in the brand table. The usecase is each has their own page: The user has a profile and the brand has a brand page. (NOTE: Thgere are more relationships like this. A company has a company page, city has city page, etc). So when users upload photos I need to reference it to the owner. Owner can be brand, user, city, organization, etc.
For user's it is straightforward, photo_id belongs to user_id where user_id is a FK to user table. But to add the brand_id as the owner of the photo, do i require a seperate colunm or can it be done in the user_id colunm itself if i rename it as owner_id which will reference (userid or brand id)?
2 columns, hands down. (One of the two must of course always be null)
With a single column you have a number of issues:
No DB-level referential constraints (= foreign key)
Risk of ID clashes between brand and user table
Confusing
You could create one mapping table for each, such as user_photos and brand_photos.
That way you have full referential integrity from user_photos to user and brand_photos to brands. This also have a possitive impact on performance for queries that filters on user/brand photos.