Leaderboard position SQL - mysql

He there,
I've found a lot of questions like this one, but I cannot seem to work it out. So I'm gonna ask it and hope that someone can help me with this specific problem (or direct me to the question if it is a duplicate).
I have the following three tables in my database:
tbl_achievements
id | points
tbl_achievements_finished
id | user_id | achievement_id
tbl_users
id
What I would like to do, is select a specific user or list of users and see the positions/ranks. The idea is that all the achievements the user finishes, yield points. The perfect outcome would be something like:
rank | user_id | achievement_points
1 | 6 | 65
2 | 3 | 45
3 | 2 | 15
I can't seem to wrap my head around it. I hope there is someone out there that can help me out with (an idea for) a query.
Thanks a lot! If anything is unclear, please let me know. :)

Something like this should do it:
SELECT af.user_id, SUM(points) as 'achievement_points'
FROM tbl_users as u
LEFT JOIN tbl_achievements_finished AS af ON u.id = af.user_id
LEFT JOIN tbl_achievements AS a ON a.id = af.achievement_id
GROUP BY u.id
ORDER BY SUM(points) DESC
This is the sample output of the above query:
| user_id | achievement_points |
|---------|--------------------|
| 3 | 40 |
| 1 | 25 |
| 2 | 15 |
See it here: http://sqlfiddle.com/#!9/69775/9/0
I didn't manage to keep the rank in place because of the ordering, but you can always have it when you process the query.

Related

Using nested SELECT result for IN statement of another nested SELECT

Be gentle. I'm a high school principal coding on the side for our school site.
I have looked at answers, here, here, and here. I might just not know enough to ask the right question.
We have events that have multiple sessions and there are workshops that can be associated with multiple sessions in multiple events.
I'm trying to get a csv result, later to be put into an array, for the associated sessions and events for my Workshops.
The query below works without the second nested Select statement.
In the Alt_Events statement, I need to pull the Event_IDs that are associated with the Session_IDs that are pulled from the first nested Select.
Events
ID | Name | Description
1 | Flex Learning | A day of flexible learning.
2 | Moonshot Expo | A day to join partners to solve problems.
Event_Sessions
ID | Event_ID | Name | Description
1 | 1 | Morning Session | The first session of the day.
2 | 1 | Afternoon Session | The afternoon session.
3 | 1 | Tutoring Session | A chance to get help from teachers.
4 | 2 | Partner Field Trip | The first session of the day.
5 | 2 | Brainstorming Session | The afternoon session.
6 | 2 | Tutoring Session | A chance to get help from teachers.
Event_Workshops
ID | Name | Description
1 | Math Tutorial | Get help from your math teachers.
Event_Workshop_Links
ID | Workshop_ID | Session_ID
1 | 1 | 3
2 | 1 | 6
Output Table:
ID | Name | Description | ... | Alt_Sessions | Alt_Events
1 | Math Tutorial | Get help... | ... | 3,6 | 1,2
Here is my query.
SELECT
ws.ID, ws.Name, ws.Description, ws.Location, ws.Owner_ID, ws.Max_Attendees,
ws.Eng_Major_Allowed, ws.Eng_Minor_Allowed,
ws.HC_Major_Allowed, ws.HC_Minor_Allowed,
ws.IT_Major_Allowed, ws.IT_Minor_Allowed,
u.LastName as Owner_LastName, u.FirstName AS Owner_FirstName, u.Email AS Owner_Email,
(SELECT group_concat(SESSION_ID) FROM Events_Workshops_Links WHERE Workshop_ID = ws.ID) AS Alt_Sessions,
(SELECT group_concat(Event_ID) FROM Event_Sessions WHERE Session_ID IN Alt_Sessions) AS Alt_Events
FROM Event_Workshops as ws
LEFT JOIN users AS u
ON ws.Owner_ID = u.ID
WHERE ws.ID = ?
ORDER BY ws.Name
I need to be able to pull the all event_ids that are in the Alt_Sessions result.
I'm guessing I can't use the result of the first nested query in the second nested query. If that's the problem, how can I pull that list of event ids?
Any and all help is greatly appreciated.
(Updated to show expected output. Also one error in transcribing the query. Session_ID instead of Event_ID in second nested statement.
Use the subquery instead of Alt_Sessions in the IN predicate like below.
(SELECT group_concat(SESSION_ID) FROM Events_Workshops_Links WHERE Workshop_ID = ws.ID) AS Alt_Sessions,
(SELECT group_concat(Event_ID) FROM Event_Sessions WHERE Session_ID IN (SELECT SESSION_ID FROM Events_Workshops_Links WHERE Workshop_ID = ws.ID)) AS Alt_Events
Also, there is a way to make combinations of Alt_Sessions and Alt_Events first and then join to Event_Workshops.
SELECT * FROM Event_Workshops ws
JOIN
(
SELECT
wsl.Workshop_ID,
GROUP_CONCAT(wsl.Session_ID) Alt_Sessions,
GROUP_CONCAT(wsl.ID) Alt_Events
FROM Event_Workshop_Links wsl
GROUP BY wsl.Workshop_ID
) w
ON ws.ID = w.Workshop_ID

Joining multiple tables SQL

The following query matches userID's to eachother based off of total score difference. I have two tables, survey & users.
I need to join this to the users table that I have that has usernames/photo links.
The columns I need displayed are users.name & users.photo. All tables currently have a unique userID, which is users.id, and survey.id that helps match users across DB's.
Could anyone give me a hand as how I could get this done? I've been having a lot of trouble figuring this out, thanks in advance.
select a.id yourId,
b.id matchId,
abs(a.q1 - b.q1) + abs(a.q2 - b.q2) + abs(a.q3 - b.q3)+ abs(a.q4 - b.q4)+
abs(a.q5 - b.q5)+ abs(a.q6 - b.q6)+ abs(a.q7 - b.q7)+ abs(a.q8 - b.q8)+
abs(a.q9 - b.q9)+ abs(a.q10 - b.q10) scorediff
from surveys as a
inner join surveys as b on a.id != b.id
WHERE a.id=1
order by scorediff asc
Currently this is the results of that query:
| yourID| matchID| scoreDiff|
----------------------------
| 5 | 2 | 14 |
| 5 | 3 | 25 |
| 5 | 1 | 33 |
| 5 | 6 | 34 |
I would like this as the result:
| yourID| matchID| scoreDiff| name | photo |
----------------------------------------------
| 5 | 2 | 14 | john | url
| 5 | 3 | 25 | steve| url
| 5 | 1 | 33 | jane | url
| 5 | 6 | 34 | kelly| url
matchID can be matched to the users.ID column, as they are all unique to the user.
add a new column with a foreign key constraint
ALTER TABLE surveys
ADD COLUMN id_user REFERENCES user(id);
or the opposite if that's what you want. Not sure if that is mysql syntax.
you can then join the tables via
WHERE u.id = s.id_user
This should (also) be a comment, but its a bit long.
on a.id != b.id
Given the logic elsewhere, this means you are going to get each combination of "surveys" listed twice. Why not:
on a.id<b.id
(note that if there is an index on index.id, this could actually result in the qquery going slower than it would in the absence of an index using both the above join expressions)
abs(a.q1 - b.q1) + abs(a.q2 - b.q2)
so you have multiple values represented as different attributes on the same relation. This is not good. It breaks the rules about normalization and makes your life much more difficult. (and ours).
Also, that you are adding the abs of the difference, to my mind, creates a rather distorted picture of the difference between individuals.
Consider:
user q score
george 1 4
symcbean 1 2
george 2 2
symcbean 2 4
Here, by your calculation there is a difference in score of 4 between the 2 users - but I would have interpreted the data above as meaning that the two users had the same score. Is that really what you intended?

Data Between Two Tables

Excuse any novice jibberish I may use to explain my conundrum but hopefully someone here will be able to look past that and provide me with an answer to get me unstuck.
SESSIONS
+--------+---------+----------+
| id | appID | userID |
+--------+---------+----------+
| 1 | 1 | 96 |
+--------+---------+----------+
| 2 | 2 | 97 |
+--------+---------+----------+
| 3 | 1 | 98 |
+--------+---------+----------+
USERS
+--------+---------+
| id | name |
+--------+---------+
| 96 | Bob |
+--------+---------+
| 97 | Tom |
+--------+---------+
| 98 | Beth |
+--------+---------+
For each session in the Sessions table that has an appID of 1, I want to get the users name from the Users table. The Sessions userID column is linked with the Users tables id column.
So my desired result would be:
["Bob", "Beth"]
Any suggestions/help?
try this:
SELECT USERS.name FROM USERS INNER JOIN SESSIONS ON users.id = SESSIONS.userID WHERE SESSIONS.appID = 1
I would read up on http://blog.codinghorror.com/a-visual-explanation-of-sql-joins/ for how all the joins work.
It looks like you forgot to post your code.
But in explanation.... It seems like you can just select the userID from the sessions table and then simply join the users table. Then create a WHERE clause to select all users that are attached to that ID.
Hope it helps.
If you post your code I can probably help you out more and if this doesnt seem just right lemme know and ill help you how i can
You need to create a join table (http://www.tutorialspoint.com/postgresql/postgresql_using_joins.htm) and then request the data using the equal operator.
SELECT USERS.name FROM USERS, SESSIONS WHERE SESSIONS.userID = USERS.ID ;

Select userids from mysql table if user has at least 3 fields filled

I have a mysql user table that holds user data like that:
userid | title | content
----------------------------------
1 | about | I am from ...
1 | location | Norway
1 | name | Mark
1 | website |
2 | about |
2 | location |
2 | name |
2 | website |
3 | ...
As you see the content is empty for userid 2, and also for many more users in the table.
My goal is to select only the userids that have at least 3 fields filled. All others should be ignored.
As my mysql knowledge is still weak I could not find a solution for this. I only found the opposite and just with count: Find the count of EMPTY or NULL columns in a MySQL table
What is the magic mysql query? Any help appreciated, thank you.
You would use aggregation and a having clause for this:
select u.userId
from users u
where content > '' and content is not null
group by u.userId
having count(*) >= 3;
I added the non-blank check as well as the null check. The null check is redundant, but it makes the intention clearer.

Finding shared list IDs in a MySQL table using bitwise operands

I want to find items in common from the "following_list" column in a table of users:
+----+--------------------+-------------------------------------+
| id | name | following_list |
+----+--------------------+-------------------------------------+
| 9 | User 1 | 26,6,12,10,21,24,19,16 |
| 10 | User 2 | 21,24 |
| 12 | User 3 | 9,20,21,26,30 |
| 16 | User 4 | 6,52,9,10 |
| 19 | User 5 | 9,10,6,24 |
| 21 | User 6 | 9,10,6,12 |
| 24 | User 7 | 9,10,6 |
| 46 | User 8 | 45 |
| 52 | User 9 | 10,12,16,21,19,20,18,17,23,25,24,22 |
+----+--------------------+-------------------------------------+
I was hoping to be able to sort by the number of matches for a given user id. For example, I want to match all users except #9 against #9 to see which of the IDs in the "following_list" column they have in common.
I found a way of doing this through the "SET" datatype and some bit trickery:
http://dev.mysql.com/tech-resources/articles/mysql-set-datatype.html#bits
However, I need to do this on an arbitrary list of IDs. I was hoping this could be done entirely through the database, but this is a little out of my league.
EDIT: Thanks for the help everybody. I'm still curious as to whether a bit-based approach could work, but the 3-table join works nicely.
SELECT a.following_id, COUNT( c.following_id ) AS matches
FROM following a
LEFT JOIN following b ON b.user_id = a.following_id
LEFT JOIN following c ON c.user_id = a.user_id
AND c.following_id = b.following_id
WHERE a.user_id = ?
GROUP BY a.following_id
Now I have to keep convincing myself not to prematurely optimize.
If you normalised your following_list column into a separate table with user_id and follower_id, then you'd find that COUNT() was extremely easy to use.
You'd also find the logic for selecting a list of followers, or a list of user's being followed much easier
Your problem would be simplified if you could split your following_list column off into a child table, e.g.
TABLE id_following_list:
id | following
--------------
10 | 21
10 | 24
46 | 45
...| ...
You can read more here.
Normalize the table, drop the column following_list, create a table following:
user_id
following_id
Which leads to the easy-peasy query (untested, you get the point):
SELECT b.user_id, COUNT(c.following)
FROM following a
JOIN following b -- get followings of <id>
ON b.following_id = a.following_id
AND b.user_id = a.following_id
JOIN following c -- get all (other) followings of <id> again, match with followings of b
ON b.following_id = c.following_id
AND c.user_id = a.user_id
WHERE a.user_id = <id>
GROUP BY b.user_id
ORDER BY COUNT(b.following) DESC
Performance may very well very based on indexes & size of dataset, maybe add a 'similarity' column which is updated at regular intervals or changes just for fast data retrieval.