mysql joining and concatinating different tables - mysql

I try to write a little voting tool. I have 3 tables: users, locations and votes. votes has 2 foreign keys (user_id and location_id).
Users (example data):
+----+----------+
| id | username |
+----+----------+
| 5 | user1 |
| 7 | user2 |
| 11 | user3 |
| 4 | user4 |
| 12 | user5 |
+----+----------+
Locations:
+----+----------------+
| id | locationname |
+----+----------------+
| 1 | Pasta |
| 2 | Burger |
| 3 | Pizza |
| 4 | Chinese |
| 5 | Thai |
+----+----------------+
Votes:
+----+---------+-------------+------------+
| id | user_id | location_id | date |
+----+---------+-------------+------------+
| 30 | 5 | 1 | 2016-06-30 |
| 31 | 5 | 1 | 2016-07-01 |
| 32 | 7 | 1 | 2016-07-01 |
| 38 | 11 | 2 | 2016-07-01 |
| 39 | 4 | 1 | 2016-07-04 |
| 41 | 12 | 3 | 2016-07-04 |
| 44 | 5 | 4 | 2016-07-04 |
| 46 | 7 | 5 | 2016-07-04 |
+----+---------+-------------+------------+
The keypair date & user is unique so a user can't vote twice.
I now want to have a list like this for CURDATE():
+----------------+----------------+----------------------+
| locationname | Votes | Voters |
+----------------+----------------+----------------------+
| Pasta | 3 | user1, user2, user x |
| Burger | 2 | user3, user4 |
| Pizza | 1 | user5 |
| Chinese | 1 | user6 |
| Thai | 0 | |
+----------------+----------------+----------------------+
How can I solve this? Tried something like that:
SELECT locations.locationname AS location, count(*) AS count, GROUP_CONCAT(users.username SEPARATOR ', ') AS Voters
FROM votes
INNER JOIN locations ON votes.location_id=locations.id
WHERE date = CURDATE()
INNER JOIN users ON users.id=votes.user_id
WHERE location_id = "1" AND date = CURDATE()
GROUP BY location_id
ORDER BY count DESC;
Thanks

A friend of mine showed me how to solve this problem:
SELECT l.id AS locationid, l.locationname, count(username) AS count, GROUP_CONCAT(username SEPARATOR ", ") AS users
FROM locations l
LEFT JOIN votes v
ON v.location_id = l.id AND v.date = CURDATE()
LEFT JOIN users u
ON v.user_id = u.id
GROUP BY locationname
ORDER BY count DESC;

Related

Querying a many to many relationship in SQL

I have the following SQL relationship
user has many games
games has may users
User
----
id | name | age |
__________________
1 | mike | 11 |
2 | jeff | 12 |
3 | jake | 31 |
4 | lemd | 81 |
Game
-----
id | name | time |
_____________________
1 | froyo | 11:10 |
2 | honey | 12:22 |
3 | combb | 13:00 |
4 | lolli | 14:00 |
User_Game
----------
| userid | game_id |
___________________
| 1 | 2 |
| 2 | 2 |
| 3 | 1 |
| 4 | 3 |
| 1 | 2 |
| 2 | 4 |
| 2 | 1 |
For each of the users is there a way to get a
list of games that they have played including the number
of games that each user participated in.
Edit
I tried this query
Select User.name, User.age
from User
inner join User_Game
on User.id=User_Game.userid;
However not sure how I could add the count to it
SELECT
userid,
GROUP_CONCAT(game_id) as game_list,
COUNT(*) as total_games
FROM
USER_GAME
GROUP BY
userid;

Creating a formulaic query with multiple columns and joined columns

I'm trying to produce a formula which pits our students' reward points against their negative behaviour flags.
Students are given LEAP points (in the transactions table) for their positive behaviour. They get more points depending on the category of their reward, i.e. Model Citizen gives the student 10 points.
On the other hand, students are given single Flags for negative behaviour. The category of the Flag is then weighted in a database table, i.e. the Aggressive Defiance category will have a high weighting of 4 whereas Low Level Disruption will only be worth 1.
The difficulty therefore is trying to factor in the Flag categories' weightings. They're stored in the categories table under the Weight column.
Here's the SQL fiddle: http://sqlfiddle.com/#!2/2e5756
In my head, the pseudo-SQL code would look something like this...
SELECT
CONCAT( stu.Surname, ", ", stu.Firstname ) AS `Student`,
SUM(t.Points) AS `LEAP Points`,
SUM(<<formula>>) AS `Flags`
( `LEAP Points` - `Flags` ) AS `Worked Out Points Thing`
FROM student stu
LEFT JOIN transactions t ON t.Recipient_ID = stu.id
LEFT JOIN flags f ON f.Student_ID = stu.id
LEFT JOIN categories c ON f.Category_ID = c.ID
GROUP BY stu.id
However, it's the <<formula>> that I have no idea how to implement in MySQL. It needs to be something like this:
SUM OF[ Each of Student's Flags * that Flag's Category Weighting ]
So, if a student has these flags...
#1 f.Reason "Being naughty", f.Category_ID "1", c.Title "Low Level Disruption", c.Weight "1"
#1 Reason "Aggressively naughty!", Category "Aggressive Defiance", Category Weighting "4"
#1 Reason "Missed detention", Category "Missed Detention", Category Weighting "3"
They would have a total of 1+4+3 = 9 points to use in the Worked Out Points Thing equation.
The desired output therefore is essentially...
Student LEAP Points Flags Equation Points LEAP Points minus Flag Points
D Wraight 1000 800 200
D Wraight2 500 800 -300
D Wraight3 1200 300 900
From the SQL fiddle above, here is the required output.. I've missed out some students because I had to work these out manually:
STUDENT FLAGS LEAP EQUATION
137608 4 (2+2) 12 (2+5+5) 8 (12-4)
139027 2 (2) 7 (2+5) 5 (7-2)
139041 4 (2+1+1+NULL) 8 (2+2+2+2) 4 (8-4)
139892 4 (4) 0 -4 (0-4)
138832 4 (4) 0 -4 (0-4)
34533 4 (4) 0 -4 (0-4)
137434 0 10 (2*5) 10 (10-0)
Which will help us to work out the choices we make available to each student when looking at end of year reward trips.
Hope that makes sense.. it's kinda boggled my head trying to explain it..
Thanks in advance,
figure out your 'formula' bit first because it's the deepest part. work outwards.
build a table of flags * weight per student
select sum(weight), student_id from flags f
join categories c
on f.category_id = c.id
group by student_id
so now you've got a table of flag values to minus from sum of transactions per student
select sum(points), recipient_id from transactions
group by recipient_id
so now we have two tables with positive and negative values by student id (assuming obviously that student id is recipient id)
you want those with transactions but without flags to appear in the result, so outer join.
and number minus null is null so ifnull function on the flags to get 0
select a.student, points - ifnull(penalties, 0) as netPoints
from
(select sum(points) as points, recipient_id as student from transactions
group by student) as a
left outer join
(select sum(weight) as penalties, student_id as student from flags f
join categories c
on f.category_id = c.id
group by student) as b
on
a.student = b.student
so with the name in there it's just
select
concat(firstname, ', ', surname) as name,
ifnull(points,0) as totalPoints,
ifnull(penalties,0) as totalPenalties,
ifnull(points,0) - ifnull(penalties, 0) as netPoints,
ifnull(countFlags, 0)
from
student
left join
(select sum(points) as points, recipient_id as student from transactions
group by student) as a
on student.id = a.student
left join
(select sum(weight) as penalties, count(f.id) as countFlags, student_id as student from flags f
join categories c
on f.category_id = c.id
group by student) as b
on
student.id = b.student
join condition is always from student's id column, which is never null.
there are probably more efficient ways, but who cares?
Returning to the question (and at the risk of repeating myself!), given the following data set, what would the desired result set look like...
SELECT * FROM flags;
+------+------------+----------+---------------------+-----------+-------------+--------------------------+---------------------+
| ID | Student_ID | Staff_ID | Datetime | Period_ID | Category_ID | Action_Taken_Category_ID | Action_Taken_Status |
+------+------------+----------+---------------------+-----------+-------------+--------------------------+---------------------+
| 8843 | 137608 | 35003 | 2014-03-11 08:31:00 | 8 | 16 | 7 | P |
| 8844 | 137608 | 35003 | 2014-03-11 08:31:00 | 8 | 16 | 7 | P |
| 8845 | 139027 | 35003 | 2014-03-11 08:31:00 | 8 | 16 | 7 | P |
| 8846 | 139041 | 35003 | 2014-03-11 08:31:00 | 8 | 16 | 7 | P |
| 8847 | 139041 | 34961 | 2014-03-11 09:01:02 | 2 | 12 | 26 | P |
| 8848 | 139041 | 34996 | 2014-03-11 09:23:21 | 3 | 12 | 27 | C |
| 8849 | 139041 | 35022 | 2014-03-11 11:07:46 | 4 | 34 | 28 | P |
| 8850 | 139892 | 138439 | 2014-03-11 11:12:47 | 4 | 21 | 7 | C |
| 8851 | 138832 | 138439 | 2014-03-11 11:12:48 | 4 | 21 | 7 | C |
| 8852 | 34533 | 138439 | 2014-03-11 11:12:48 | 4 | 21 | 7 | C |
+------+------------+----------+---------------------+-----------+-------------+--------------------------+---------------------+
SELECT * FROM categories;
+----+------+--------------------------------------+--------+----------+
| ID | Type | Title | Weight | Added_By |
+----+------+--------------------------------------+--------+----------+
| 10 | F | Low level disruption | 1 | NULL |
| 11 | F | Swearing directly at another student | 2 | NULL |
| 12 | F | Late | 1 | NULL |
| 13 | F | Absconded | 3 | NULL |
| 14 | F | Refusal to follow instruction | 3 | NULL |
| 15 | F | Smoking | 2 | NULL |
| 16 | F | No homework | 2 | NULL |
| 17 | F | Disruptive outside classroom | 2 | NULL |
| 18 | F | Eating/drinking in lesson | 1 | NULL |
| 19 | F | Incorrect uniform/equipment | 1 | NULL |
| 20 | F | Phone out in lesson | 3 | NULL |
| 21 | F | Aggressive defiance | 4 | NULL |
| 22 | F | Missed detention | 3 | NULL |
| 23 | F | Inappropriate behaviour/comments | 3 | NULL |
| 32 | F | IT Misuse | NULL | NULL |
| 34 | F | Inappropriate attitude towards staff | NULL | NULL |
| 35 | F | Care & Guidance | NULL | NULL |
+----+------+--------------------------------------+--------+----------+
SELECT * FROM transactions;
+----------------+------------+----------+--------------+--------+-------------+
| Transaction_ID | Datetime | Giver_ID | Recipient_ID | Points | Category_ID |
+----------------+------------+----------+--------------+--------+-------------+
| 34 | 2011-09-07 | 35019 | 137608 | 2 | 1 |
| 35 | 2011-09-07 | 35019 | 139027 | 2 | 1 |
| 36 | 2011-09-07 | 35019 | 139041 | 2 | 1 |
| 37 | 2011-09-07 | 35019 | 139041 | 2 | 1 |
| 38 | 2011-09-07 | 35019 | 139041 | 2 | 1 |
| 39 | 2011-09-07 | 35019 | 139041 | 2 | 1 |
| 40 | 2011-09-07 | 35019 | 137434 | 2 | 1 |
| 41 | 2011-09-07 | 35019 | 137434 | 2 | 1 |
| 42 | 2011-09-07 | 35019 | 137434 | 2 | 1 |
| 43 | 2011-09-07 | 35019 | 137434 | 2 | 1 |
| 44 | 2011-09-07 | 35006 | 137434 | 2 | 1 |
| 45 | 2011-09-07 | 35006 | 90306 | 2 | 1 |
| 46 | 2011-09-07 | 35006 | 90306 | 2 | 1 |
| 47 | 2011-09-07 | 35006 | 90306 | 2 | 1 |
| 48 | 2011-09-07 | 35023 | 137608 | 5 | 2 |
| 49 | 2011-09-07 | 35023 | 139027 | 5 | 2 |
| 50 | 2011-09-07 | 35023 | 139564 | 5 | 2 |
| 51 | 2011-09-07 | 35023 | 139564 | 5 | 2 |
| 52 | 2011-09-07 | 35023 | 139564 | 5 | 2 |
| 53 | 2011-09-07 | 35023 | 137608 | 5 | 3 |
+----------------+------------+----------+--------------+--------+-------------+
SELECT id,UPN,Year_Group,Tutor_Group,SEN_Status,Flags FROM student;
+--------+---------------+------------+-------------+------------+--------+
| id | UPN | Year_Group | Tutor_Group | SEN_Status | Flags |
+--------+---------------+------------+-------------+------------+--------+
| 137608 | A929238400044 | 11 | 11VID | A | |
| 139027 | A929238401045 | 10 | 10KS | | |
| 139041 | A929238402017 | 10 | 10RJ | A | FSM |
| 139892 | A929238403018 | 9 | 9BW | | |
| 139938 | A929238403020 | 9 | 9RH | | |
| 137434 | A929238500027 | 11 | 11VID | | |
| 138832 | A929238502002 | 10 | 10RY | A | FSM,PA |
| 34533 | A929238599028 | 0 | | | PA |
| 139564 | A929241500025 | 12 | | | PA |
| 90306 | A929253100006 | 12 | SLH | A | PA |
+--------+---------------+------------+-------------+------------+--------+

MYSQL add row and count from diffrent table

I have mysql database with two tables.
First (information)
+---------+------+----------+
| species | sex | user |
+---------+------+----------+
| bird | NULL | 1 |
| bird | f | 1 |
| cat | f | 1 |
| cat | m | 1 |
| dog | f | 1 |
| dog | m | 2 |
| hamster | f | 2 |
| snake | m | 1 |
+---------+------+----------+
Second (users)
+--------+-----+
| user | id |
+--------+-----+
| amy | 1 |
| dav | 2 |
| mot | 3 |
| mmm | 4 |
| aaw | 5 |
| dsa | 6 |
+--------+-----+
I want to count and show values from table "information" for each user row on table "users"
Like this
+---------+------+----------+
| user | id | count |
+---------+------+----------+
| amy | 1 | 6 |
| dav | 2 | 2 |
| mot | 3 | 0 |
| mmm | 4 | 0 |
| aaw | 5 | 0 |
| dsa | 6 | 0 |
+---------+------+----------+
How can I do this query?
select users.user, users.id, count (species.name)
from users left join species
on users.id = species.user
group by users.user, users.id
order by count (species.name) desc
Isn't it something like:
select u.user, u.id, count(i.user)
from user u
inner join information i on i.user = u.id
group by u.user, u.id

redisplaying data from a table in mySQL

I am trying to redisplay the data from a table
currently the data in the table goes as
__________________________________________
| UserMeta |
|__________________________________________|
| id | userID | orgID | keyType | keyData |
|____|________|_______|__________|_________|
| 1 | 1 | 1 | UserName | Scarlet |
| 2 | 1 | 1 | Age | 23 |
| 3 | 2 | 2 | UserName | Diana |
| 4 | 2 | 2 | Age | 27 |
| 5 | 3 | 1 | UserName | Kevin |
| 6 | 3 | 1 | Age | 18 |
|____|________|_______|__________|_________|
I'd like to be able to format it to the following
_________________________________
| vUserMeta |
|_________________________________|
| UserID | orgID | UserName | Age |
|________|_______|__________|_____|
| 1 | 1 | Scarlet | 23 |
| 2 | 2 | Diana | 27 |
| 3 | 1 | Kevin | 18 |
|________|_______|__________|_____|
One approach would be to join UserMeta on itself as follows:
SELECT u.userID AS UserID,
u.orgID AS orgID,
u.keyData AS UserName,
v.keyData AS Age
FROM UserMeta u
JOIN UserMeta v
ON u.userID = v.userID
AND u.keyType = 'UserName'
AND v.keyType = 'Age'

LEFT JOINs with multiple tables

I have following tables:
table users - PRIMARY KEY (user_id)
+---------+----------+-----------+
| user_id | username | realname |
+---------+----------+-----------+
| 1 | peterpan | Peter Pan |
| 2 | bobfred | Bod Fred |
| 3 | sallybe | Sally Be |
| 6 | petersep | Peter Sep |
+---------+----------+-----------+
table users_groups - PRIMARY KEY (user_id, group_id)
+---------+----------+
| user_id | group_id |
+---------+----------+
| 1 | 1 |
| 1 | 2 |
| 2 | 1 |
| 2 | 2 |
| 3 | 6 |
| 3 | 9 |
| 6 | 6 |
| 6 | 9 |
+---------+----------+
table game - PRIMARY KEY (id)
+----+-------+
| id | game |
+----+-------+
| 1 | Game1 |
| 2 | Game2 |
| 6 | Game6 |
| 9 | Game9 |
+----+-------+
table groups - PRIMARY KEY(group_id)
+----------+--------------+---------------+
| group_id | group_name | group_desc |
+----------+--------------+---------------+
| 1 | Groupname1 | Description1 |
| 2 | Groupname2 | Description2 |
+----------+--------------+---------------+
table group_game - PRIMARY KEY(group_id, game_id)
+----------+----------+
| group_id | game_id |
+----------+----------+
| 1 | 1 |
| 1 | 2 |
| 2 | 6 |
| 2 | 9 |
+----------+----------+
I want to display this (like a group list):
+----+------------+--------------+---------------------+--------------+
| id | group name | group desc | group members | group games |
+----+------------+--------------+---------------------+--------------+
| 1 | GroupName1 | Description1 | Peter Pan, Bob Fred | Game1, Game2 |
| 2 | GroupName2 | Description2 | Sally Be, Peter Sep | Game6, Game9 |
+----+------------+--------------+---------------------+--------------+
Now I have this query but it gives me no rows (no error, just zero rows):
SELECT
g.group_name,
g.group_id,
g.group_desc,
GROUP_CONCAT(DISTINCT ga.game SEPARATOR ', ') AS games,
GROUP_CONCAT(DISTINCT u.realname SEPARATOR ', ') AS users
FROM groups g
LEFT JOIN users_groups ug1
ON g.group_id=ug1.group_id
LEFT JOIN users u
ON ug1.user_id=u.user_id
LEFT JOIN group_game gg
ON g.group_id=gg.group_id
LEFT JOIN game ga
ON gg.game_id=ga.id
GROUP BY g.group_name
How can I solve this problem or how can I write this query?
I just want to show a group list with all information (like group information, users of the groups, games of this group).