I'm writing an app where people can upload lists of movies they own.
My database will probably have the following structure:
USER
UserID - unique identifier
Name - user's full name
Email - user's email
MOVIE
UserID - user who owns this movie
MovieID - movie unique identifier (I'm currently using IMDB's code)
So "Movie" has many-to-one relationship with "User"
What I would like to do is to recommend a user of a random "movie buddy". Basically, I would like to find 2 users who own the same movie (so I'm trying to match 2 user's list to find matching movies) and then showing him a message similar to "Hey, XYZ is a fan of The Dark Knight Rises as well!"
The match should be random with a simple filter for already seen suggestions (I don't want a user to get the same suggestion twice for both the same buddy and movie, but it's ok to suggest him the same buddy for a DIFFERENT movie - assuming they both own 2 different movies).
I don't know how to perform this kind of match using MySQL. I'm also open to other suggestion for a database if one is more appropriate (I've heard about graph databases, but never used any myself)
Thanks.
Finding users with the same movie ID is pretty simple its just three JOINs
FROM
User u
INNER JOIN movie m
ON u.UserID = m.UserID
INNER JOIN moive BuddyMovie
ON m.moiveID = BuddyMovie.MovieID
and u.UserID <> Buddy.UserID --Don't want to be your own Buddy
INNER JOIN User BuddyUser
ON BuddyMovie.UserID = BuddyUser.UserID
WHERE
u.UserID = 123 -- For a given user
and m.MovieID = 345 -- Optionally For a given movie
Getting a random user you just need to add ORDER BY RAND()
If you want only two just add LIMIT 2
Making sure you don't get the same Buddy Twice will require that you keep track of which buddies were all ready shown which goes beyond the scope of a single question.
Related
I am getting students added to course using a query but i noticed 2 of them do not appear( even if they are active and listed as participants) checking inside tables i noticed they do not appear in mdl_user_info_data. How can i prevent this? or whats the reason they dont were added to this table
This is my query:
SELECT u.id,u.username,u.firstname,u.lastname,u.email, b.data
FROM mdl_user u,mdl_role_assignments r, mdl_user_info_data b
WHERE u.id=r.userid
AND u.id=b.userid
AND r.roleid=5
AND r.contextid = 'somecontextId'
ORDER BY u.email ASC;
The table mdl_user_info_data holds the values for custom user profile fields (that are defined in mdl_user_info_field).
If a user's profile has not been edited since a particular custom user profile field has been created, then there will not be an associated mdl_user_info_data record for them.
Note that if there is more than one custom user field defined, there can be more than one mdl_user_info_data field - so your query could return more than one record per user.
You probably want to rewrite your query to LEFT JOIN with mdl_user_info_data. You probably also want to LEFT JOIN with mdl_user_info_field to identify which of the custom user profile fields it relates to.
Also note that your query makes a number of assumptions that may not always be true - if your query is running inside Moodle code, then you should use {user_info_data} instead of the 'mdl_' prefix, as that prefix can be changed. Hard-coding roleid 5 for 'student' can also fail on some sites (although it is usually the case).
I saw a lot of questions here but no one fits with my problem. I'm trying to create an ER model scalable, and if I want to add more data don't break almost anything, so what I've trying to create is :
There are 2 types of users, let's say Admin and Worker, and they have different roles.
Admin can do a CRUD of questions, and also can create a room where the users can join to play together (this is just a name, something like Kahoot! does) but maybe is a good idea to create more attributes inside of it like, WHO is playing in this room, POINTS for everyone but let's talk it afterwards when I show you the design.
Ok the thing is, on my design I have :
Table User which contains :
_id
username
password
date_creation
This is a default one, but then I'm wondering how do I define the Role if it's an Admin or a Worker, something like isAdmin:true and then I check this Bool? Or I can create another table that is Role and connect it to User table?
But maybe I have to create a table for both, I mean there's an Admin which has a password, and some functionalities and then ther'es the user Worker which has another password and another functionalities.
Then I'd like to have the Question table where contains :
_id
question_name
answers[1,2,3,4]
correctAnswer or answers because it can be multi option chooser
topic
isExamQuestion
dificulty
Then the Room table should contains :
_id
name
capacity
type (game can be as a group or solo) that's why this attribute
exam (This should be a flag to know if this question is for an exam or not (It can be for EXAM or PRACTISE)
ranking (This is the top X from 1 to X)
don't think if I have to add the winner here because if I get the position 0 from ranking I get the winner...
There's a table named Topic as well, if my question have a topic then I can select the question by Topic. An example of Topic should be Math so user can do only exams or do tests with math questions.
_id
Name
Questions[...]
Then I have to store like a historic about what are the questions worker has answered correct and what did not, to make some statistics, but I need to store some historicals for Admin to see in this topic the average that Workers have failed more is : Question23 (for instance) something like that.
What I'm missing, could you try to help me to figure it out how to make this design better?
NOTE : I'm using Spring for server side, Angular for Frontend stuff, and Android for App, I can change anything to work faster/better with this database though.
EDIT
There's the flow of the game if you need more details and if I'm explainted wrong .
Admin flow
Create questions (with different kinds of answers like True/false, with a checkbos (single and multianswer), text, etc...)
Create a "game" where workers can join (This is mostly programming stuff) but it should be a room with attributes there, like id of the room, maxNumber, type (exam), and store historicals, theres also a type of game (for instance, images, videos, etc..)
See statistics about Workers it means see how many answers they answered correct, failed, see per topic (This is like joins and stuff, but the design has to be good done)
See historic of the exams that he did before with all of the information (participant, score, time, stuff)
And the Worker flow is
He can practise means that he's answering questions randomly or by topic (every single answer should be saved for statistics and to avoid repeat the ones he respons correct), also he can do exams (not multiplayer) just an option that Admin can check if the question is part of an exam or not.
And then the room stuff, he can join with the Id.
If you need further clarification let me know and I'll reply you as soon as possible.
In fact, your system has three logical parts (modules):
users module that contains user data and implements authentication and the authorization of user actions
questionnaires module that includes management of questions and answer
questionnaires history module that contains history by each user
Database design of those modules can look as follows
USER MODULE:
role - contains user roles in the system
id - unique identifier of the role
name - the role name, for example, admin, worker, etc.
user - contains users and information about roles were assigned to them
id - unique identifier of the user
username
password
role_id - identifier the role was assigned to the user
QUESTIONNAIRES MODULE:
topic - contains question themes
id - unique identifier of the theme
name - a name of the theme
question - contains questions
id - unique identifier of the question
topic_id - topic identifier of the question
text - content of the question
is_exam_question - exam question or not
type - type of answers (boolean, checkboxes or etc.)
difficulty
answer - contains all answers of questions
id - unique identifier of the answer
question_id - identifier of the question that contains the answer
text - content of the question
is_correct - flag that means the answer is true or false
room - contains information about rooms
id - unique identifier of the rum
name - name of the rum
capacity - the maximum number of workers which can join to the room
type - room type: group, solo or etc.
learing_type - room type: exam, practice or etc.
user_in_room - contains information about users which were joined to the room
user_id - identifier of the user that was joined to the room
room_id - identifier of the room
score - current score of the user in the room
HISTORY MODULE:
user_question_history - contains information about questions which were answered by the user
user_id - identifier of the user
room_id - identifier of the room where the user answered questions
question_id - identifier of the question that was answered by the user
score - user score by the question
user_answer_history - contains information about answers which were chosen by the user
user_id - identifier of the user
room_id - identifier of the room where the user answered questions
question_id - identifier of the question that was answered by the user
answer_id - identifier of the answer that was chosen the user
Usage of this schema gives the ability to build different reports. For example, you can display the result of all users by room
SELECT r.id,
r.name,
u.username,
ur.score
FROM room as r
LEFT JOIN user_in_room as ur ON ur.room_id = r.id
LEFT JOIN user as u ON u.id = ur.user_id
WHERE r.id = <id>
Or you can see detail information about answers of the user
SELECT
q.text,
a.text
FROM user_in_room as ur ON ur.room_id = r.id
LEFT JOIN user_question_history as uqh ON ugh.user_id = ur.user_id AND ugh.root_id = ur.room_id
LEFT JOIN question as q ON q.id = ugh.question_id
LEFT JOIN user_answer_history as uah ON uah.user_id = ugh.user_id AND uah.room_id = ugh.room_id AND uah.question_id = ugh.question_id
LEFT JOIN answer as a ON a.id = uah.answer_id
WHERE ur.room_id = <id> AND ur.user_id = <id>
Honestly, if you're certain you'll only have to possible types--both now and in the future--a simple bool isAdmin in your user table will work nicely. Everything else the admin does can be handled from the programming side. No need to clog up your db.
That said, if there's even a small chance you will have other types of users in the future, Maxim's answer is the way to go. That way, adding another role such as "Editor," for instance, is a simple as adding a record to the "Role" table. In fact, adding 1000 new types users is as simple as adding records to your "Role" table. Because you already have a table that you look to for the role, your code doesn't have to worry about all the possible types of users (which is a pain if you have a lot of them), only the ones it needs. The db is handling the rest.
One drawback to Maxim's answer is that it takes more work to implement in the db, and more work to view/update the role of the user.
To implement in the db, you need to create a whole extra table and make sure it's linked properly. This isn't hard, but, especially if you're new to dbs, is extra work that might not be worth it to you. This also means, from a maintenance side, that you have another table to keep tabs on. Not a huge deal, but, again, something to think about.
From the code side, this creates extra chores as well. For one, the user type is no longer directly in your users table--an ID is. This means when you want to know the name of the user type, you will have to either a) query the db for that user type based on the ID you have, or b) join the 2 tables, which can be tricky at times. Updating the role also has similar chores.
Again, these aren't huge problems, just things to think about. It might not be worth the extra work if you will only have two possible options.
So, in short, a boolean will work and is simple to implement, but is not scalable. Maxim's answer is quite scalable and makes future expansion easier, but somewhat harder to implement and maintain. You'll have to make the decision what you prefer.
You need a Profile table with one-to-many with User table, this way if another privelege you want to apply, just add new profile entry:
User table:
Id
Username
Fullname
Profile_id
...
Profile table:
Id
Name
Description
Exam and Question tables are related with many-to-many, to break it you will have a third table Question_Exam:
Question_Exam:
id_exam
Id_Question
Answer(provided)
Id_user(taking the exam)
IsCorrect(boolean, to avoid farther queries )
date
Topic and Question are one-to-many
Question table:
Id
Name
Answer(The correct one)
Id_topic
The other structure is fine.
I have been working on a Social Network to further my knowledge with PHP, however I have come to the point where I will be pulling posts from he database depending on who the user is "friends" with.
My database structure for friends is as follows:
USER_A | USER_B | CREATED_AT | IS_BLOCKED
And then for the posts:
USER | UPDATE | STATUS
So, I want to show posts selected from my post table if the user logged in is friends with the user who posted it.
So, basically, wondering if anyone has any suggestions. It won't be anymore complicated than that, not interested in getting friends of friends or anything like that. So, just wondering if someone could point me in the right direction with this one.
I read it would require JOINs and such, however I have never really stepped into that side of SQL so therefore would have no idea what I would be doing with it, so links to relevant questions and articles on that, if required, would be appreciated.
Thanks
(I am essentially looking for help with the SQL statement, not with the PHP.)
Update #1:
I have got the newsfeed working, now I need to be able to join a statement to get the users names to be displayed. My current status is on this: http://sqlfiddle.com/#!2/b07793/1/0
Before anyone goes crazy about storing passwords in plaintext, this is not a public system, it is purely for me to test, and hashing passwords for a non-public script that will only ever be used by me with fake passwords is hardly a security risk, it just makes debugging a pain.
Something like this should work :
SELECT * FROM posts
JOIN friends ON posts.user = friends.user_b
WHERE user_a = 'someuser'
In this statement we are JOINING to tables : posts and friends, based on the value of two column, we are putting in relation the column user of the post table with the column user_b of the friends table, in this way we will obtain a result set with only the value of the two table where the two columns in relation are equal ...
If I understood correctly the db schema you described, this will bring you all posts that have been written by a user that is friends with the logged in user.
SELECT * FROM posts WHERE posts.user IN (SELECT user_b FROM friendships WHERE user_a=$logged_in_user)
The query is pretty simple. You get all the rows from table posts that the creator of that post has a line in your friendships table as user_b while user_a is set to be the logged in user.
I'm assuming a few things, if they're not correct you'll have to modify this stuff but if I'm right, these operations won't need any JOINs:
$userSession variable contains the user who's logged in's ID.
$userToView variable contains the user who he's trying to view's ID.
Your database structure will only have one entry for the two users who are friends, so we have to check both combinations.
To determine if the user is a friend:
SELECT user_a, user_b FROM friends
WHERE (user_a = $userSession AND user_b = $userToView)
OR (user_a = $userToView AND user_b = $userSession)
LIMIT 1;
Then you'd use an if statement in PHP (it's usually better to keep business logic in the application, rather than in the database), if it's true return the posts.
$results = $mysqli->query($query)->fetch_row();
if($results) {
... return posts ...
} else {
echo 'Not friends';
}
That would be a query something like this:
SELECT * FROM posts
WHERE user = $userToView;
You will need to read up about mysqli (do not use mysql plugin, it's deprecated) and about SQL injection attacks and how to prevent them. It may sound like a hassle, but it's far less daunting to understand this stuff than to deal with the fallout if you don't.
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.
I am currently working as a programmer in a small startup aiming on providing pay-per-view content online and I have been assigned to develop the metadata database for the movie catalogue
I have two main tables, movie and people, where *movie_ID* and *people_ID* are the primary keys respectively for each table. Both tables have a many-to-many relationship.
To represent different relations I am currently using link tables, for example, actor_movie would store the *movie_ID* and corresponding *people_ID* for each of the actors in the movie, while the director_movie table would store the *movie_ID* and the director(s) *people_ID*. Same goes for writer, composers and producers.
Now, my problem is that I need to craft out a query that returns all the actors, directors, producers, writers, composers, etc. etc. in one single table to be passed on the frontend Web UI as a list of all the persons involved in the movie.
I'm currently stumped as to how to create a multiple SELECT query that would JOIN all the link tables together based on the *movie_ID* and *people_ID* and then return the details of each of the person in the people table as well.
And example of what I have written so far is:
SELECT
movie.titleMovie,
people.namePeople,
FROM
movie movie
INNER JOIN actorlinkmovie acm ON acm.idMovie = movie.idMovie
INNER JOIN people people ON people.idPeople = acm.idPeople
What I would like to have happen is:
SELECT
movie.idMovie,
movie.titleMovie,
movie.descMovie,
movie.dateMovie,
movie.runtimeMovie,
movie.langMovie,
movie.ratingMovie,
people.namePeople
FROM
htv_movie movie
INNER JOIN htv_actorlinkmovie acm ON acm.idMovie = movie.idMovie
INNER JOIN htv_directorlinkmovie dcm ON dcm.idMovie = movie.idMovie
INNER JOIN htv_producerlinkmovie pcm ON pcm.idMovie = movie.idMovie
INNER JOIN htv_people people WHERE people.idPeople = dcm.idPeople AND people.idPeople = acm.idPeople AND people.idPeople = pcm.idPeople
And it should return the all the related people from a single movie.
Would like to get some input about the whole design since I'm a pretty new at designing a whole database (first time actually) and whether would this design be suitable if I need to scale up to about 5000 movies (the current company aim). This database will pretty much serve as the website's backend as well.
Thanks.
UPDATE: Temporarily worked out a dirty solution using PHP variables and a template SQL query. Looks like doing multiple inner joins wasn't that required after all. Thanks for the suggestions though.
You can achieve your goal like this:
SELECT MovieName,
dbo.GetDirector(MovieID),
dbo.GetActors(MovieID),
dbo.GetWriter(MovieID) FROM Movie
where
dbo.GetDirector(MovieID) is a
function that will return directors
in the movie.
dbo.GetActors(MovieID) is a function
that will return actors in the movie.
dbo.GetWriter(MovieID) is a function
that will return writers in the
movie.
If there are some other tables then you can make functions for those tables as well.
Hope this helps.