Database model for individual users seeing notice - mysql

I am looking for the best solution for the way the mySQL db should be set up for my app.
My app works like a noticeboard with two sections, "New Notices" and "Seen Notices".
Now when a user has viewed a notice, they click a button and it moves from New to Seen. But ONLY for this person.
Each person will have all of the notices viewable - but not necessarily in the same sections - as users will view them at different times and check them off as seen at different times.
My guess is having one table "Notices" for all notices, and a seperate table called "Seen" with the rows "UserID" and "noticeID". This means that for each notice it will need to consult the "Seen" table to find out if it should be shown or not. Is this ideal or is there another way?

Having a table with NoticeID and UserID is correct, I'd also add viewed date.

You can use 3 tables
Users
Notices
SeenNotices(maybe not the best name)
In the SeenNotices table have three columns UserID, NoticesID, HaveSeen. The have HaveSeen column will tell you if the user has seen it.

The way you are thinking should work, although over time you'll end up with a very big 'Seen' table, which is not scalable. An easy alternative is to use 'Unseen' table instead. This way the table gets smaller as people view the notice and you can also delete very old entries (old notices may no longer relevant so doesn't matter if they are not shown as Unseen to user).
Using the 'unseen' table your query will look like this:
SELECT n.notice_id, n.notice_msg, IF(u.user_id, 'new', 'seen') AS status
FROM notice n
LEFT JOIN unseen u ON (u.user_id = $user_id AND n.notice_id = u.notice_id)
WHERE user_id = $user_id;

Related

SQL: Seeking an advise for tables structure etc

First what are conditions. I have people belonging to "small" group. (which in other words means every one has "small_group_id". Then "small" groups form "big" groups (which in other words means "small_groups" may or not have "big_group_id" depending if small group belongs to bigger ot not).
I want to create a table structure (that would be used by PHP) for keeping and displaying two following things:
Public messages (means whoever is regestered or even not will be able to see it). Only author of the message can edit/delete. This is easy part :)
Private messages WITH defining how private is it. That means privat emessage should have property a) what small groups can see it b) what big groups can see it (that assumes that all members of big groups will have rights to see it).
Basically the challenge for me is how to design and later work with visibility of those private messages.
My first though was table like: msgID, msgBody, small_groups_list, big_group_list, authorID So I store e.g. in 'small_groups_id' something like 'id_1; id_4; id_10', etc and similar for big groups. But then I'm not sure how do I do search through such stored lists when e.g. person belonging to small_group_id = 10 supposed to see that mesage. Also what should be the columns small_groups_list and big_group_list defenitions/types.
Perhaps there is better way to store such things and using them as well?
That is why I'm here. What would be better practices for such requirements?
(it is going to be implemented on mySQL)
Thank you in advance.
[edit]
I'm pretty unexperienced in SQL and DB things. Please take that into account when answering.
First: Don't denormalize your data with "array" columns. That makes it a horror to query, and even worse to update.
Instead, you need two separate tables: small_group_visibility and big_group_visibility. Each of these two tables will consist of msgID and groupID. Basically, it's a many-to-many relationship that's pointing out to both the group and the message it is concerned with.
This is a pretty common database pattern.
To query for messages to be displayed, imagine that we have a user whose small groups are (1, 2, 3) and whose large groups are (10, 20).
SELECT DISTINCT msgID, msgSubject, msgBody -- and so on
FROM messages m
LEFT JOIN small_group_visibility sg
ON sg.msg_id = m.msg_id
LEFT JOIN big_group_visibility bg
ON bg.msg_id = m.msg_id
WHERE
sg.group_id IN (1, 2, 3) OR
bg.group_id IN (10, 20);

How inefficient would this query be and also table structure?

I have two tables. One table stores course_updates, which basically has a new row pushed to it everytime someone adds or drops a course. I also have another table follower_updates that has a record pushed to it whenever someone follows someone. I want to be able to get the information for the logged in user, but I'm unsure how I should detect which table the information is coming from being that I want to display the information based upon which table it is from. Should I make a new column that update_type, or should I have a different method?
I'm also going to show what I'm thinking relatively in terms of sql. It won't be perfect because I haven't tested it yet. This is just a sample. I didn't want to bring in my current query because just the course_updates already has three inner joins and an outerjoin, so I tried to streamline the content for this question. thanks!
SELECT * FROM course_updates WHERE (establishes connection
enter code herebetween user and courses and followers)
UNION SELECT * FROM follower_updates WHERE followee.id = currentUser.id etc.
Don't use a UNION. Use two separate queries instead.

How to efficiently design MySQL database for my particular case

I am developing a forum in PHP MySQL. I want to make my forum as efficient as I can.
I have made these two tables
tbl_threads
tbl_comments
Now, the problems is that there is a like and dislike button under the each comment. I have to store the user_name which has clicked the Like or Dislike Button with the comment_id. I have made a column user_likes and a column user_dislikes in tbl_comments to store the comma separated user_names. But on this forum, I have read that this is not an efficient way. I have been advised to create a third table to store the Likes and Dislikes and to comply my database design with 1NF.
But the problem is, If I make a third table tbl_user_opinion and make two fields like this
1. comment_id
2. type (like or dislike)
So, will I have to run as many sql queries as there are comments on my page to get the like and dislike data for each comment. Will it not inefficient. I think there is some confusion on my part here. Can some one clarify this.
You have a Relational Scheme like this:
There are two ways to solve this. The first one, the "clean" one is to build your "like" table, and do "count(*)'s" on the appropriate column.
The second one would be to store in each comment a counter, indicating how many up's and down's have been there.
If you want to check, if a specific user has voted on the comment, you only have to check one entry, wich you can easily handle as own query and merge them two outside of your database (for this use a query resulting in comment_id and kind of the vote the user has done in a specific thread.)
Your approach with a comma-seperated-list is not quite performant, due you cannot parse it without higher intelligence, or a huge amount of parsing strings. If you have a database - use it!
("One Information - One Dataset"!)
The comma-separate list violates the principle of atomicity, and therefore the 1NF. You'll have hard time maintaining referential integrity and, for the most part, querying as well.
Here is one way to do it in a normalized fashion:
This is very clustering-friendly: it groups up-votes belonging to the same comment physically close together (ditto for down-votes), making the following query rather efficient:
SELECT
COMMENT.COMMENT_ID,
<other COMMENT fields>,
COUNT(DISTINCT UP_VOTE.USER_ID) - COUNT(DISTINCT DOWN_VOTE.USER_ID) SCORE
FROM COMMENT
LEFT JOIN UP_VOTE
ON COMMENT.COMMENT_ID = UP_VOTE.COMMENT_ID
LEFT JOIN DOWN_VOTE
ON COMMENT.COMMENT_ID = DOWN_VOTE.COMMENT_ID
WHERE
COMMENT.COMMENT_ID = <whatever>
GROUP BY
COMMENT.COMMENT_ID,
<other COMMENT fields>;
[SQL Fiddle]
Please measure on realistic amounts of data if that works fast enough for you. If not, then denormalize the model and cache the total score in the COMMENT table, and keep it current it through triggers every time a new row is inserted to or deleted from *_VOTE tables.
If you also need to get which comments a particular user voted on, you'll need indexes on *_VOTE {USER_ID, COMMENT_ID}, i.e. the reverse of the primary/clustering key above.1
1 This is one of the reasons why I didn't go with just one VOTE table containing an additional field that can be either 1 (for up-vote) or -1 (for down-vote): it's less efficient to cover with secondary indexes.

Store Voting Information - Database Outline

Summary: What is the most efficient way to store information similar to the like system on FB. Aka, a tally of likes is kept, the person who like it is kept etc.
It needs to be associated with a user id so as to know who liked it. The issue is, do you have a column that has a comma delimited list of the id of things that were liked, or do you have a separate column for each like (way too many columns). The info that's stored would be a boolean value (1/0) but needs to be associated with the user as well as the "page" that was liked.
My thought was this:
Column name = likes eg.:
1,2,3,4,5
Aka, the user has "like" the pages that have an id of 1, 2, 3, 4 and 5. To calculate total "likes" a tally would need to be taken and then stored in a database associated with the pages themselves (table already exists).
That seems the best way to me but is there a better option that anyone can think of?
P.S. I'm not doing FB likes but it's the easiest explanation.
EDIT: Similar idea to the plus/neg here on stackoverflow.
In this case the best way would be to create a new table to keep track of the likes. So supposing you have table posts, which has a column post_id which contains all the posts (on which the users can vote). And you have another table users with a column user_id, which contains all the users.
You should create a table likes which has at least two columns, something like like_postid and like_userid. Now, everytime a user likes a post create a new row in this table with the id of the post (the value of post_id from posts) that is liked and the id of the user (the value of user_id from users) that likes the post. Of course you can enter some more columns in the likes table (for instance to keep track of when a like is created).
What you have here is called a many-to-many relationship. Google it to get some more information about it and to find some more advice on how to implement them correctly (you will find that a comma seperated lists of id's will not be one of the best practices).
Update based on comments:
If I'm correct; you want to get a list of all users (ordered by name) who have voted on an artist. You should do that something like:
SELECT Artists.Name, User.Name
FROM Artists
JOIN Votes
ON Votes.page_ID = Artists.ID
JOIN Users
ON Votes.Votes_Userid = Users.User_ID
WHERE Artists.Name = "dfgdfg"
ORDER BY Users.Users_Name
There a strange thing here; the column in your Votes table which contains the artist id seems to be called page_ID. Also you're a bit inconsistent in column names (not really bad, but something to keep in mind if you want to be able to understand your code after leaving it alone for 6 months). In your comment you say that you only make one join, but you actually do two joins. If you specify two table names (like you do: JOIN Users, Votes SQL actually joins these two tables.
Based on the query you posted in the comments I can tell you haven't got much experience using joins. I suggest you read up on how to use them, it will really improve your ability to write good code.

database schema of "don't show me again what I saw before" in mysql

I'm making a website that shows you images. And special feature of site is "don't show me again what I saw before". It means, if you see a image, it goes to your "archive" category. There will be so many images and categories. And I need to very smooth schema of database to perfomance.
When you click a image, it appears on lightbox and in the lightbox code it sends request with ajax to make this image archived just for you.
Is that database schema above performanceful for about 5.000 images and 20.000 users?
users
user_id
user_email
pictures
picture_id
picture_url
tags
archived
user_id
picture_id
images will appear on front of you with excepting archived images for you from all images on this schema...
This is a diificult question to answer without knowing all the details. You mention how many users and images there will be. How many images will each user (on average) have in their archived list? If that number is small, the archived table won't approach 100M rows.
100M rows should not be a problem by itself, as the database can handle this. The concern may (or will) be with the way you are going to want to query the data. Something like:
SELECT
*
FROM
picture
WHERE
picture_id NOT IN
(
SELECT picture_id FROM archived WHERE user_id = [userIdParameter]
)
That will likely not perform very well with 100M rows.
Another option would be to cross join users and pictures so that the archived table always contains a Cartesian product. So the table would be:
archived
user_id
picture_id
visited
Then you could query like so:
SELECT
p.*
FROM
picture p
INNER JOIN archived a ON p.picture_id = a.picture_id
WHERE
a.user_id = [userIdParameter]
AND a.visited = [false]
This should perform acceptably with proper indexing, but would present the problem of having to make sure rows are created in the archived table any time a user or picture is added to the system. It also means you would always have a number of rows equal to pictures * users (100M in your example). That may not be desirable in your case.
Bottom line, you are going to have to create some test data that approximates your expected volume and do some performance testing that approximates your load. If you think this is the critical potential performance bottleneck for your system, it will be worth the time investment.
I used "NOT IN" solution for a while and there is performance problems started. Because I don't have a strong server to execute that query with lot of datas.
So, I found the most performanceful answer : "Collection Shuffle"
I'm shuffleing the collection with a userid seed and saving just users last image index id. After user comes back, looking to where this user's index id left lastly, showing next id from his collection.
This is really light and exactly solution. Thanks for everyone :)