How to get random unread article? - sql

Database table articles contains 10.000 rows (articles)
I want to get a random article, mark it as read and never get it again,
so my next get request should return random article except that one.
There are thousands of users like me and all of them are using this table.
How do I implement this? How do I mark those articles, and how do I search only unread articles?
I was trying to create the column relatedUsers in the Articles table, which is filled out with relations to user objects, who has read the article. I was using search query: relatedUsers.objectId!=currentUserId But when this column get 2+ relatedUsers, this solutions stops working, because the database returns articles, which has at least one relatedUser, who's objectId isn't equal to currentUserId (means all users).
I'm using Backendless.com right now, but if I guess, the solution should be applicable to any database, including backendless. If not, then what kind of database should I use for this?

What you can do now is:
Create a table named UserArticle with fields articleId and userId
When you add a user or an article, you should also add to this table relations to all articles or users respectively; this way your UserArticle table will contain the relations between articles and users who haven't yet read those articles
And in order to get a random not read article for user, you:
Retrieve items from UserArticle where userId = yourCurrentUserId
Randomly select one of them and retrieve an article by its ID
Remove the selected article from UserArticle by articleId and userId, meaning that the user has read the article
This approach is the most suitable in your case. It involves a little more than one request, but for now your requirements cannot be fit better.
Also we (the Backendless team) are working on a keyword like contains, using which you would be able to have only a relation to Users table and get not read articles in one request, so the suggested approach is pretty temporary.

Getting a random number depends on the database. But the basic idea is to have a table called UserArticles with one row per user and per article already read (and perhaps other information such as the time).
Then, you can do:
select a.*
from articles a
where not exists (select 1
from userarticles ua
where a.article_id = ua.article_id and ua.user_id = $user_id
)
order by rand()
limit 1;
The order by rand() limit 1 is definitely database-dependent. But, it gives you the idea of how to approach the problem.
Once you have selected the article, then you insert this information into UserArticles:
insert into UserArticles(user_id, article_id)
values ($user_id, $article_id);
where $article_id refers to the article retrieved in the previous step.
Note: this should be fine with respect to race conditions, unless a single user can have multiple simultaneous connections to the database asking for the same information. Handling that case requires more knowledge about the database being used.

Related

SQL - how to get out chained data?

I have 4 tables which were auto generated for me:
User
Challenge
Exercise
Challenge_Exercise
One User may have many Challenges, and one Challenge will have many Exercises.
What I noticed is that the Challenge table has a reference to it's parent User (called user_id) but Exercise do not have a reference in it's table to Challenge; their relation is stored in Challenge_Exercise as Challenge_id and exercise_id.
My question is, how would I take out every Exercise that is linked to a specific user? For instance User with id = 1?
SELECT *
FROM excerise,
challenge_excerise,
challenge
WHERE challenge.user_id = 1
AND challenge_excerise.challenge_id = challenge.id
AND challenge_excerise.exercise_id = excercise.id
What I'm doing here is a join, you could also explicitly do it with inner joins (google it if you wanna know more).
This table is needed because you have a many to many relationship, which means each challenge can have multiple exercises, but also each exercise can have multiple challenges. It's a standard to make an extra table then, so you don't have redundant data, this table is often called junction table.
If you want background just google it, there are tons of data to this topic.

schema for big database

I was trying to solve this issue recently, but i don't really know how.
I have an application that allows users to register and create profiles(as many as they want).
For every profile they can create campaigns(as many as they want per profile) and for each campaign they can add links(there is a limited number for the links but it's big anyway). Each link can have it's own keywords (more than 1 keyword).
The obvious thing that came to my mind was to have a table for users, one for profiles, one for campaigns, one for links and one for keywords. But think of this, some users may use the same keywords and i don't want to repeat that information over the database n times. I don't know if this is possible in mysql but i would like to have a field in the links table which will refer to the ids of the keywords in the keywords table. something like an array of ids. I would like this implementation to be flexible, allowing me to easily retrieve the keywords, update the "array of keywords" and perform certain computations (count the number of keywords for example). Can you recommend a possible solution on how to implement this?
Just to state again: I'm using mySQL and php.
Thank you.
from that description i thought of these tables:
user (id, ...)
campaigns (id, user_id, ...)
links (id, campaign_id, link)
keywords (link_id, keyword)
You should create a table to store the keywords i.e.
id (int)
keyword (varchar)
And store an association table for links -> keywords i.e.
link_id (int)
keyword_id (int)
Hope this helps!
Christian
I argue that your initial implementation of a table for each of the entities is correct. If you store keywords in a separate table and associate them with a link_id or something like that then you can look up links with common keywords much faster than an array containing all the keywords for each link.
I would argue that althought its possible that the same keyword may be choosen for different links by a user. This does not make them semantically the same.
If if have a campaign for driftwood and flotsom and use "shells" as a keyword on a link this is not the same "shells" as I would use as a keyword on the unix utilities campaign.
Stick with your original clean and logical schema and dont complicate it by solving imaginary problems.
You need to have a many to many table which stores the ID of the link, the id of the user and the id of the keyword (I'm assuming all links have keywords)
Then you can accomplish what you're talking about just through normal database operations.
There are 2 ideas
1) have each user have their own set of keywords
have a user table and have keywords in another table with userID as a FK.
When a user no matter what profile/campaign they are in needs to add a link you display the keywords for that user.
The link would still link to a keywordID via a join table that would hold keywordID and LinkID
2) global keywords
have keywords just have keywordID and keyword
there would be a join table to hold keywordID and LinkID, allowing a link to have multiple keywords.
The front end would then have to be made to ensure the users search existing keywords before adding new ones, this would help prevent double ups. The process that adds a keyword should also check for an existing value before adding

Database structure for voting system with up- and down votes

I am going to create a voting system for a web application and wonder what the best way would be to store the votes in the (SQL) database.
The voting system is similiar to the one of StackOverflow. I am pondering now if I should store the up and down votes in different tables. That way it is easier to count all up votes resp. down votes. On the other hand I have to query two tables to find all votes for an user or voted item.
An alternative would be one table with a boolean field that specifies if this vote is an up or down vote. But I guess counting up or down votes is quite slow (when you have a lot of votes), and an index on a boolean field (as far as I know) does not make a lot of sense.
How would you create the database structure? One or two tables?
Regarding the comments, we found the solution that best fits to Zardoz
He does not want to always count votes and needs as much details as possible. So the solution is a mix of both.
Adding an integer field in the considered table to store vote counts (make sure there won't be overflows).
Create additional tables to log the votes (user, post, date, up/down, etc.)
I would recommend to use triggers to automatically update the 'vote count field' when inserting/deleting/updating a vote in the log table.
If your votes are just up/down then you could make a votes table linking to the posts and having a value of 1 or -1 (up / down). This way you can sum in a single go.
https://meta.stackexchange.com/questions/1863/so-database-schema
Worth a look or
http://sqlserverpedia.com/wiki/Understanding_the_StackOverflow_Database_Schema
You will need a link table between users and the entities which are being voted on, I would have thought. This will allow you to see which users have already voted and prevent them from submitting further votes. The table can record in a boolean whether it is an up or down vote.
I would advise storing in the voted entity a current vote tally field to ease querying. The saving in size would be negligible if you omitted this.

Recommended Table Set up for one to many/many to one situation

I need to create a script where someone will post an opening for a position, and anyone who is eligible will see the opening but anyone who is not (or opts out) will not see the opening. So two people could go to the same page and see different content, some potentially the same, some totally unique. I'm not sure the best way to arrange that data in a MySQL DB/table.
For instance, I could have it arranged by the posting, but that would look sort of like:
PostID VisibleTo
PostingA user1,user2
And that seems wrong (the CSV style in the column). Or I could go with by person:
User VisiblePosts
user1 posting1, posting2
But it's the same problem. Is there a way to make the user's unique, the posting unique, and have them join only where they match?
The decision is initially made by doing a series of queries to another set of tables, but once that is run, it seems inefficient to have that some chunk of code run again and again when it won't change after the user posts the position.
...On second thought, it MIGHT change, but if we assume it doesn't (as it is unlikely, and as little consequence if a user sees something that they are no longer eligible for), is there a standard solution for this scenario?
Three tables...
User:
[UserId]
[OtherField]
Post:
[PostId]
[OtherFields]
UserPost:
[UserId]
[PostId]
User.UserId Joins to UserPost.UserId,
Post.PostId Joins to UserPost.PostId
Then look up the table UserPost, joining to Post when you are selecting which posts to show
This is a many-to-many relationship or n:m relationship.
You would create an additional table, say PostVisibility, with a column PostID and UserID. If a combination of PostID and UserID is present in the table, that post is visible to that user.
Edit: Sorry, I think you are speaking in Posting-User terms, which is many-to-many. I was thinking of this in terms of posting-"viewing rights" terms, which is one-to-many.
Unless I am missing something, this is a one-to-many situation, which requires two tables. E.g., each posting has n users who can view it. Postings are unique to an individual user, so you don't need to do the reverse.
PostingTable with PostingID (and other data)
PostingVisibilityTable with PostingID and UserID
UserTable with UserID and user data
Create the postings independently of their visibility rights, and then separately add/remove PostingID/UserID pairs against the Visibility table.
To select all postings visible to the current user:
SELECT * FROM PostingTable A INNER JOIN PostingVisibilityTable B ON A.PostingID = B.PostingID WHERE B.UserID = "currentUserID"

What is the best way to add users to multiple groups in a database?

In an application where users can belong to multiple groups, I'm currently storing their groups in a column called groups as a binary. Every four bytes is a 32 bit integer which is the GroupID. However, this means that to enumerate all the users in a group I have to programatically select all users, and manually find out if they contain that group.
Another method was to use a unicode string, where each character is the integer denoting a group, and this makes searching easy, but is a bit of a fudge.
Another method is to create a separate table, linking users to groups. One column called UserID and another called GroupID.
Which of these ways would be the best to do it? Or is there a better way?
You have a many-to-many relationship between users and groups. This calls for a separate table to combine users with groups:
User: (UserId[PrimaryKey], UserName etc.)
Group: (GroupId[PrimaryKey], GroupName etc.)
UserInGroup: (UserId[ForeignKey], GroupId[ForeignKey])
To find all users in a given group, you just say:
select * from User join UserInGroup on UserId Where GroupId=<the GroupId you want>
Rule of thumb: If you feel like you need to encode multiple values in the same field, you probably need a foreign key to a separate table. Your tricks with byte-blocks or Unicode chars are just clever tricks to encode multiple values in one field. Database design should not use clever tricks - save that for application code ;-)
I'd definitely go for the separate table - certainly the best relational view of data. If you have indexes on both UserID and GroupID you have a quick way of getting users per group and groups per user.
The more standard, usable and comprehensible way is the join table. It's easily supported by many ORMs, in addition to being reasonably performant for most cases. Only enter in "clever" ways if you have a reason to, say a million of users and having to answer that question every half a second.
I would make 3 tables. users, groups and usersgroups which is used as cross-reference table to link users and groups. In usersgroups table I would add userId and groupId columns and make them as primary key. BTW. What naming conventions there are to name those xref tables?
It depends what you're trying to do, but if your database supports it, you might consider using roles. The advantage of this is that the database provides security around roles, and you don't have to create any tables.