compare mysql numeric values group_concat of two columns with join - mysql

I have 3 tables
1.users
user_id nationality
1 Egyptian
2 Palestinian
3 French
centers
id center_name
1 q
12 y
5 x
23 z
centers_users
student_id center_id
1 12
2 5
3 5
1 23
2 12
what I expect
Nationality center_name count_of_users_from this country
Egyptian y,z 10
Palestinian x,y 33
French x,q 7
I have tried many mysql queries but I cannot get the result I want
Final query I execute:
SELECT * from (SELECT (LENGTH(GROUP_CONCAT(DISTINCT user_id))-ENGTH(REPLACE(GROUP_CONCAT(DISTINCT user_id), ',', ''))) as ss,GROUP_CONCAT( DISTINCT user_id) ,nationality from user where user_id in(SELECT student_id FROM `centers_users`) GROUP by nationality)a
But only get the count with nationality.
When I Join with centers gives me redundancy because I cannot put "ON" condition with
group_concat
How can I implement it?
Thanks..

I think you want to join the tables and aggregate:
select u.nationality,
group_concat(distinct c.center_name) as center_names,
count(distinct user_id) as users_from_this_country
from users u join
user_centers uc
on u.user_id = uc.student_id join
centers c
on c.center_id = uc.center_id
group by u.nationality;
You may be able to use count(*) for users_from_this_country. It depends on how you want to count a user who is in multiple centers in the same country.

Related

SQL distinct elements count in a group by same element

I have a problem to count distinct elements in a group by this element.
Let me explain this, I have two tables:
tb1
team amount
1 90
2 80
3 70
4 50
5 60
tb2
team player
5 1
1 1
3 2
1 2
2 2
1 3
3 3
4 3
5 3
2 4
The expected result is:
player nb_team Sum_amount nb_player
1 2 150 3
2 3 240 4
3 4 270 3
4 1 80 2
I'm doing this:
SELECT tb2.player, COUNT(DISTINCT tb1.team) as nb_team,
SUM(tb1.amount) AS sum,
(SELECT COUNT(DISTINCT tb2.player)
FROM tb2 where tb1.team=tb2.team) AS nb_player
FROM tb1, tb2
WHERE tb1.team=tb2.team
GROUP BY tb2.player
ORDER BY tb2.player ASC;
The 3 first columns are correct but I can't get the right value for nb_player.
I have to count how many players are included by the number of teams
For example for the first line result:
player 1 is playing in 2 teams that involved 3 players in total (player #1,#2 and #3)
any idea?
Counting teams and summing those teams amounts for a player needs a different resultset than counting players playing with given player in the same team. So I suggest using two different subqueries and then joining them on the player.
SELECT teams_total.player, teams_total.nb_team, teams_total.`sum`,
players_total.nb_players
FROM
( SELECT tb2.player, COUNT(DISTINCT tb1.team) as nb_team,
SUM(tb1.amount) AS `sum`
FROM tb1 JOIN tb2 ON tb1.team=tb2.team
GROUP BY tb2.player ) teams_total
JOIN
( SELECT tb2_1.player, COUNT(DISTINCT tb2_2.player) as nb_players
FROM tb2 tb2_1
JOIN tb1 ON tb2_1.team=tb1.team
JOIN tb2 tb2_2 ON tb2_2.team=tb1.team
GROUP BY tb2_1.player ) players_total
ON teams_total.player=players_total.player
ORDER BY teams_total.player ASC;
You can use the following query:
SELECT player,
COUNT(DISTINCT t2.team) AS nb_team,
SUM(amount) AS Sum_amount,
(SELECT COUNT(DISTINCT(player))
FROM tb2 AS t
WHERE INSTR(CONCAT(',',GROUP_CONCAT(DISTINCT t2.team), ','),
CONCAT(',',t.team,',')) <> 0) AS nb_player
FROM tb2 AS t2
INNER JOIN tb1 AS t1 ON t2.team = t1.team
GROUP BY player
GROUP_CONCAT is used in the correlated sub-query in order to get a comma separated list of all teams related to the player of the outer query. Using INSTR on this list, we can filter tb2 rows and count the DISTINCT number of players of these teams.
Demo here

Multiple join with self join in MySQL and split rows in columns by a row value

I have three tables "Users" , "Subjects" and "Marks" like
Users Table
id name
1 A
2 B
3 C
4 D
5 E
6 A
7 B
Subjects Table
id name
1 Chemistry
2 Physics
3 English
4 Maths
5 History
Marks Table
u_id is the foreign key of Users (id) and s_id is foreign key of Subjects(id)
id u_id s_id marks
1 1 1 60
2 1 2 70
3 1 3 80
4 2 2 80
5 2 3 44
6 3 1 50
7 5 4 50
8 4 5 50
9 5 4 100
10 2 5 100
and I wish for the result to be like
id Name Chemistry Physics English
1 A 60 70 80
2 B NULL 80 44
3 3 50 NULL NULL
Using Join
So far I have only been able to get
name name marks
A English 80
A Physics 70
A Chemistry 60
B English 44
B Physics 80
C Chemistry 50
Using the following query
SELECT u.name, s.name , m.marks
FROM Users as u
RIGHT JOIN Marks as m ON m.u_id = u.id
LEFT JOIN Subjects as s ON m.s_id = s.id
WHERE s.name = "English"
OR s.name = "Physics"
OR s.name = "Chemistry"
ORDER BY u.name; "
Well, after reading the answers, I wanted to post my own one:
SELECT
u.id
, u.name
, MAX(IF(s.id = 1, COALESCE(m.mark), 0)) as 'Chem'
, MAX(IF(s.id = 2, COALESCE(m.mark), 0)) as 'Phys'
, MAX(IF(s.id = 3, COALESCE(m.mark), 0)) as 'Eng'
FROM marks m
INNER JOIN subjects s
ON s.id = m.subjects_id
INNER JOIN users u
ON u.id = m.users_id
GROUP BY u.id
You can check that makes all you want in SqlFiddle: http://sqlfiddle.com/#!9/f567b/1
The important part is the grouping of all the elements according to the user id, and the way of writing the results from rows in a table to columns in another table. As written in #TheShalit answer, the way of achieving that is just assigning the value as a column. Problem is that when grouping by user, you'll have a lot of values there from where you have to select the important one (the one that is not 0 neither NULL, XD). COALESCE function makes sure that you always return a integer, just in case a NULL is given.
It's also important to notice that you'll have to build the SQL with the names of the subjects and the ids from database, as SQL can't retrieve the name of the elements to write them directly as names of the columns. That's why I wrote 'Chem', 'Phys' and 'Eng' instead of the right names. In fact, would be easier if you just wrote the id of the subject instead of a name, just to retrieve the elements later when you'll fetch the rows.
Take into account that is VERY IMPORTANT that you'll table will have the right indexes there. Make sure you have an UNIQUE id on the table marks with users and subjects to avoid having more than one value there stored
Use select like this(with joins and group by student):
MAX(If(subjects.name="Chemistry",marks.marks,'')) Chemistry,
MAX(If(subjects.name="Physics",marks.marks,'')) Physics,
.....
You will need to do something like:
SELECT u.NAME AS NAME,
m_e.marks AS english,
m_p.marks AS physics,
m_c.marks AS chemistry
FROM users AS u
JOIN marks AS m_e ON m_e.u_id = u.id
JOIN marks AS m_p ON m_p.u_id = u.id
JOIN marks AS m_c ON m_c.u_id = u.id
WHERE m_e.s_id = 3 AND m_c.s_id = 1 AND m_p.s_id = 2
You are getting 3 different values from a single table but different rows so you need to join the marks table with itself to be able to get the values from 3 different records into 1 result row
I used the values that you defined as primary id's for your 3 subjects in your question in the where clause to make sure you are getting the correct result for each subject

SQL Incorrect SUMS from multiple JOINS

I'm trying to sum multiple tables using Joins and Sums in MySQL and not having much success.
My Tables (Unnecessary Columns Removed)
Students
idStudent studentname studentyear
1 foobar 11
2 barfoo 11
3 thing 8
Athletics_Results
idResult idStudent points
1 1 14
2 1 11
3 3 7
4 2 9
Team_Results
idTeamResults year points
1 11 9
2 8 8
3 7 14
So let me explain about the tables, because I admit they're poorly named and designed.
Students holds the basic info about each student, including their year and name. Each student has a unique ID.
Athletics_Results stores the results from athletics events. The idStudent column is a foreign key and relates to idStudent in the student column. So student foobar (idStudent 1) has scored 14 and 11 points in the example.
Team_Results stores results from events that more than one student took part in. It just stores the year group and points.
The Aim
I want to be able to produce a sum of points for each year - combined from both athletics_results and team_results. EG:
year points
7 14 <-- No results in a_r, just 14 points in t_r
8 15 <-- 7 points in a_r (idResult 4) and 8 in t_r
11 43 <-- 14, 11, 9 points in a_r and 9 in t_r
What I've tried
For testing purposes, I've not tried combining the a_r scores and t_r scores yet but left them as two columns so I can see what's going on.
The first query I tried:
SELECT students.studentyear as syear, SUM(athletics_results.points) as score, SUM(team_results.points) as team_score
FROM students
JOIN team_results ON students.studentyear = team_results.year
JOIN athletics_results ON students.idStudent = athletics_results.idStudent
GROUP BY syear;
This gave different rows for each year (as desired) but had incorrect SUMS. I learnt this was due to not grouping the joins.
I then created this code:
SELECT studentyear as sYear, teamPoints, AthleticsPoints
FROM students st
JOIN (SELECT year, SUM(tm.points) as teamPoints
FROM team_results tm
GROUP BY year) tr ON st.studentyear = tr.year
JOIN (SELECT idStudent, SUM(atr.points) as AthleticsPoints
FROM athletics_results atr
) ar ON st.idStudent = ar.idStudent
Which gave correct SUMS but only returned one year group row (e.g the scores for Year 11).
EDIT - SQLFiddle here: http://sqlfiddle.com/#!9/dbc16/. This is with my actual test data which is a bigger sample than the data I posted here.
http://sqlfiddle.com/#!9/ad111/7
SELECT tr.`year`, COALESCE(tr.points,0)+COALESCE(SUM(ar.points),0)
FROM Team_Results tr
LEFT JOIN Students s
ON tr.`year`=s.studentyear
LEFT JOIN Athletics_Results ar
ON s.idStudent = ar.idStudent
GROUP BY tr.year
According to your comment and fiddle provided
check http://sqlfiddle.com/#!9/dbc16/3
SELECT tr.`year`, COALESCE(tr.points,0)+COALESCE(SUM(ar.points),0)
FROM (
SELECT `year`, SUM(points) as points
FROM Team_Results
GROUP BY `year`) tr
LEFT JOIN Students s
ON tr.`year`=s.studentyear
LEFT JOIN Athletics_Results ar
ON s.idStudent = ar.idStudent
GROUP BY tr.year
Try this http://sqlfiddle.com/#!9/2bfb1/1/0
SELECT
year, SUM(points)
FROM
((SELECT
a.year, SUM(b.points) AS points
FROM
student a
JOIN at_result b ON b.student_id = a.id
GROUP BY a.year) UNION (SELECT
a.year, SUM(a.points) AS points
FROM
t_result a
GROUP BY a.year)) c
GROUP BY year;
On your data I get:
year points
7 14
8 15
11 43
Can be done in multiple ways. My first thought is:
SELECT idStudent, year, SUM(points) AS totalPoints FROM (
SELECT a.idStudent, c.year, a.points+b.points AS points
FROM students a
INNER JOIN Athletics_Results b ON a.idStudent=b.idStudent
INNER JOIN Team_Results c ON a.studentyear=c.year) d
GROUP BY idStudent,year

SQL: Fetch rows having a column (group by column) being the MAX value

I would like to know how to retrieve rows matching the maximum value for a column.
SCHEMA
assignments:
id student_id subject_id
1 10 1
2 10 2
3 20 1
4 30 3
5 30 3
6 40 2
students:
id name
10 A
20 B
30 C
subjects:
id name
1 Math
2 Science
3 English
Queries:
Provide the SQL for:
1. Display the names of the students who have taken most number of assignments
2. Display the names of the subjects which have been taken the most number of times
Results:
1.
A
C
2.
Math
English
Thanks !
The previous answer is not quite right - you won't get the instances where there are two with the same count. Try this - the second will be easy to replicate once understand the concept.
SELECT a.student_id, s.name, COUNT(a.subject_id) as taken_subjects
FROM assignments a
INNER JOIN students s ON a.student_id = s.id
GROUP BY a.student_id, s.name
HAVING COUNT(a.subject_id) = (SELECT COUNT(*) FROM assignments GROUP BY student_id LIMIT 1)
Alternate query:
SELECT a.subject_id, s.subject_name, COUNT(a.subject_id) FROM assignment a, subjects s
WHERE a.subject_id = s.subject_id
GROUP BY a.student_id, s.subject_name
HAVING COUNT(a.subject_id) = (SELECT MAX(COUNT(1)) FROM assignment GROUP BY subject_id)

How can you decide which records of a JOINED table remains after a GROUP BY?

I'm trying to LEFT JOIN two tables and GROUP BY a field of the first table in MySQL.
If there are multiple rows in the second table for each record in the first one, a GROUP BY eliminates all records but one of the right table. Is there a way to determine which one it is?
To give you a specific example, I want to get a list of users, joined with the IDs of their (for example) most expensive purchases (or most recent purchases, or whatever..) It seems like an easy task, but I'm frustrated and have asolutely no idea how to do it!
Table 1: Users
userId, userName
1 Frank
2 Sarah
3 Tim
Table 2: Purchases
orderId, userId, value
1 3 14.99
2 2 9.99
3 3 79.99
4 1 2.99
5 2 14.99
SELECT * FROM Users LEFT JOIN Purchases ON Users.userId = Purchases.userId
will return:
userId, userName, orderId, value
1 Frank 4 2.99
2 Sarah 2 9.99
2 Sarah 5 14.99
3 Tim 1 14.99
3 Tim 3 79.99
Now if I GROUP BY userId the result will be:
userId, userName, orderId, value
1 Frank 4 2.99
2 Sarah 2 9.99
3 Tim 1 14.99
Is there a way to decide in this case which orderId is kept or is there a completely other and better way to do this?
I have tried some things like MAX() but this will always only return the highest value of the whole table, not individually for each user.
Thank you in advance, you awesome stackoverflow-community!
best Florian
In strict SQL this Query would not be valid as in a Group by context u should select only fields contained in the group by clause or aggregates.
Mysql however allows this syntax and handles it as "i dont care about this fields", you can not define which of the rows values is selected then.
But you can do it with a query like this:
SELECT u.*,p.* FROM Users u LEFT JOIN
( SELECT userId, max(value) as max_value FROM Purchases GROUP BY userId) p ON u.userId = p.userId
If you want to get the max() pusrchase per user preserving the order like orderid etc you may need to do this
select
u.userId,
u.userName,
p.orderId,
p.value
from Users u
inner join Purchases p on
p.userId = u.userId
inner join
(
select orderId,
max(value) as value,
userId
from Purchases
group by userId
)o
on o.userId = p.userId
AND o.value = p.value
group by u.userId
http://sqlfiddle.com/#!2/1afda/9