MySQL: Combine multiple row values into single column? - mysql

Database: MySQL
Table: Teams
TeamID INT
TeamName VARCHAR(20)
Table: People
PeopleID INT
FirstNameID INT
LastNameID INT
Table: TeamMembers
TeamID INT
PeopleID INT
Table Example: Teams
TeamID TeamName
1 Team Xstream
2 Team INsanity
Table Example: People
PeopleID FirstNameID LastNameID
1 1351 453
2 5463 763
3 976 8762
4 87 784
5 187 465
6 761 566
7 376 2134
Table Example: TeamMembers
TeamID PeopleID
1 1
1 3
1 7
2 2
2 4
2 5
2 6
Desired Output:
TeamName TeamMembers
Team Xstream John Smith/Jane Doe/Daniel Davis
Team INsanity Sally Sue/Tom Thomas/Jack Jones/Harry Henderson
There will not be a set number of TeamMembers per team, so it's not like I could have three subqueries because there will only be three team members. I've lightly looked online, but I always get the best and most thorough answers here. Any ideas or pointers, please let me know. I honestly have no idea where to begin here. Thanks.

I am guessing that the names of the people are actually stored somewhere considering you are just showing ID numbers as names. But you will want to use both CONCAT() and GROUP_CONCAT() for this result. The first step, will join all of the tables and use the CONCAT() function:
select t.teamname,
concat(p.FirstNameId, ' ', p.LastNameId) teamMembers
from teams t
left join teammembers m
on t.teamid = m.teamid
left join people p
on m.peopleid = p.peopleid;
See SQL Fiddle with Demo, which will produce the result:
| TEAMNAME | TEAMMEMBERS |
-------------------------------
| Team Xstream | 1351 453 |
| Team Xstream | 976 8762 |
| Team Xstream | 376 2134 |
| Team INsanity | 5463 763 |
| Team INsanity | 87 784 |
| Team INsanity | 187 465 |
| Team INsanity | 761 566 |
Once you have the data, then apply the GROUP_CONCAT() function and GROUP BY the teamname:
select t.teamname,
group_concat(concat(p.FirstNameId, ' ', p.LastNameId) SEPARATOR '/') teamMembers
from teams t
left join teammembers m
on t.teamid = m.teamid
left join people p
on m.peopleid = p.peopleid
group by t.teamname;
See SQL Fiddle with Demo
results:
| TEAMNAME | TEAMMEMBERS |
---------------------------------------------------
| Team INsanity | 761 566/87 784/187 465/5463 763 |
| Team Xstream | 976 8762/376 2134/1351 453 |

You didn't provide information on the table where names are actually stored, but what you are looking for is GROUP_CONCAT function. This is how you would use it to show the first and last name id's. I will leave it to you to join the name table and replace the name id fields in the query with the actual name fields.
SELECT t.TeamName, GROUP_CONCAT(CONCAT(p.FirstNameID, ' ', p.LastNameID))
FROM Teams as t
INNER JOIN TeamMembers as tm on t.TeamID = tm.TeamID
INNER JOIN People as p on tm.PeopleID = p.PeopleID
GROUP BY t.TeamName
By the way, normalizing the names out into their own table and using nameID seems like probably a good example of over normalization. Why not just have name values in People table? This will save you adding a fourth table to your query (and possibly fifth table if first and last names are in different tables).

Related

Writing complex MySQL Join query

This may be simple for some, but I cannot work it out.
I have 3 tables:
Teams, Users, Tags
Teams Users Tags
------------------- ------------------ -----------------------
userID | teamUserID userID | username userID | name | value
------------------- ------------------ -----------------------
1 | 2 1 | dan 2 | myTag | 1
1 | 3 2 | bob 2 | aTag | 2
1 | 4 3 | jon 3 | bTag | 1
4 | rob 4 | cTag | 5
Each team can have a number of users in it, and each user can own a number of tags.
I need a query which will provide a list of users in any given team, with a total number of tags they have.
So when I request results from team 1 (dan's team) it should return this:
-----------------------------------
userID | username | tagTotalValue
-----------------------------------
2 | bob | 3
3 | jon | 1
4 | rob | 5
I have this query so far, but it just gives me one record with an overall total for the whole team, rather than a list of all the users in the team separately with their totals.
SELECT username, SUM(value) tagTotalValue
FROM users u LEFT JOIN tags t
ON u.userID = t.userID
WHERE u.userID IN (SELECT teamUserID FROM teams WHERE userID = 1)
Help!
If anyone can explain a good way of working out how to build these queries, I would be very grateful to learn. Do I just need to do a mySQL course, or is there a simple method I can employ?
I need a query which will provide a list of users in any given team,
with a total number of tags they have.
This seems to have little to do with the query you have written. You should start by joining the three tables together and then aggregating. The query looks something like this:
SELECT t.teamId, u.userId, u.username, count(ta.userId) as numTags
FROM teams t JOIN
users u
ON t.teamUserID = u.UserId LEFT JOIN
tags ta
ON u.userID = ta.userID
WHERE t.teamId = #teamId -- this can be removed
GROUP BY t.teamId, u.userId, u.username;
This query makes the leap that teams has a column that identifies the team -- say teamId.

adding rows from the same table that match a criteria in a inner join result

I'm new to programming.
This is assignment work..... AND this is also my first ever SO post !!
Hello world! :)
I've hit a wall trying to use a join/union or ("other" method??) on the results of an inner join query with additional rows from the same table (based on a matching secondary ID).
I'm creating a view with the output.
I have a member table as m
Each member is denoted as part of a family group with family_ID
Each family has a primary contact (primaryContact_ID) person who pays membership fees. membership payments for each primarycontact_ID are in a membershippayment table as mp.
I wish to select all members who are considered paid in a certain year. Note: this means that where the family primary contact has paid, other family members are therefore considered paid / current.
So first I can select paid primary contacts easily. using:
SELECT m.memberNumber, m.firstName, m.lastName, m.family_ID , mp.yearCurrentTo
FROM member m
INNER JOIN membershippayment mp
on m.primaryContact_ID = mp.primaryContact_ID
WHERE mp.yearCurrentTo = 2015
The output is
memberNumber | firstName | lastName | family_ID | yearCurrentTo
6 | ted | smith | 2 | 2015
But Ted has 3 other family members ( also in the members table m) as denoted by family_ID = "2"
How do I (select or join or union or "Other") additional family members with m.family_ID as Ted ?
The family_id value will be dynamic and have 1 or more values, depending on the inner join result set.
i have tried adding an inner join to the original member table using a second alias :
SELECT m.memberNumber, m.firstName, m.lastName, m.family_ID, mp.yearCurrentTo
FROM member m
INNER JOIN membershippayment mp
on m.primaryContact_ID = mp.primaryContact_ID
INNER JOIN member m2
on m2.family_ID = m.family_ID
WHERE mp.yearCurrentTo = 2015
But all I get is Teds details repeated 3 more times.
memberNumber | firstName | lastName | family_ID | yearCurrentTo
6 | ted | smith | 2 | 2015
6 | ted | smith | 2 | 2015
6 | ted | smith | 2 | 2015
6 | ted | smith | 2 | 2015
it should be each additional family member, of unique memberNumber but matching family_ID
I've also tried a union but get similar repeated rows.
From here i'm stumped.
Can it all be achieved in the same query ? I'm thinking using nesting . But I get lost attempting this.
or should I use an updateable view and do a separate select and update query.
Thanks !

Comparing between several fields and a table

Say I've got the next to tables: Drugs, and Clients:
idNum | drugName
118 | drug1
118 | drug2
120 | drug1
120 | drug2
120 | drug3
121 | drug1
121 | drug3
122 | drug2
Clients:
idNum | Name | lastName |
118 | name1 | last1 |
119 | name2 | last2 |
120 | name3 | last3 |
121 | name4 | last4 |
122 | name5 | last5 |
I want to create a new table containing all id's, Names and last names of clients who took the same drugs as client with the idNum='118'
Which means i need to get a table with only '120'.
So far i've got:
SELECT Clients.idNum,firstName, lastName
FROM Clients, Drugs
WHERE Drugs.idNum=Clients.idNum
AND Clients.idNum<>'118'
AND Clients.idNum IN
(Select idNum From Drugs Where drugName IN
(Select drugName from Drugs where idNum='118'))
But this gives me also 121 and 122.
I guess 'IN' is something like 'Exists', So if someone has only 1 of the drugs the condition is enough. How do i actually compare between a table which i get from here:
(Select drugName from Drugs where idNum='118')
to a field in 'Drugs'?
Or maybe, How do i create a table for each of the customers and then compare it with the table?
This is a tricky query. The idea is to generate the list of drugs for each client that matches client 118. This is the "master" list. Then, use left outer join to bring in the actual drugs for each client. When a match fails, then filter out the client:
select master.idNum, master.firstName, master.lastName
from (select d.drugName, c.*
from Drugs d cross join
Clients c
where d.idnum = 118
) master left outer join
Drugs d
on master.drugName = d.drugName and
master.idNum = d.idNum
group by master.idNum, master.firstName, master.lastName
having count(d.drugName) = count(master.drugName);
The SQL Fiddle is here. If you want to additionally filter out client 118, then change the nested where clause to something like d.idnum = 118 and c.idnum <> 118.
You should also learn proper join syntax and the use of table aliases.

MySQL query to get multiple values from 1 table based on multiple columns from other table

I have this table structure (let's call this table teams):
id | player1 | player2 | player3
1 | 13 | 25 | 50
And this (let's call this table players):
id | pts
13 | 5
25 | 10
50 | 12
Is there a way to write one query to get the pts vlaue for each of the players from a row in table teams?
For instance, if I wanted to get the pts value for player1, I would do something like:
select teams.id,players.pts from teams left join players on teams.player1=players.id
I need to do this for all 3 players at once (in reality, there are 18 players, not 3 - but the same principle should work).
I'm aware I can loop through them but that hardly seems like the optimal solution.
I think a better idea would be to have the following structure:
teams
team_id | name
----------------
1 | Team 1
2 | Team 2
3 | Team 3
players
player_id | pts
---------------
1 | 10
2 | 5
3 | 25
teams_players_xrefs
team_id | player_id
-------------------
1 | 1
1 | 2
2 | 3
And then use the following query:
SELECT
player_id,
pts
FROM
teams_players_xrefs
INNER JOIN
players USING (player_id)
WHERE
team_id = 1
To get the following result:
player_id | pts
---------------
1 | 10
2 | 5
Here's the SQL Fiddle.
SELECT
player1.pts AS player1_pts,
player2.pts AS player2_pts,
player3.pts AS player3_pts
FROM
teams
LEFT JOIN players AS player1 ON team.player1=player1.id
LEFT JOIN players AS player2 ON team.player2=player2.id
LEFT JOIN players AS player3 ON team.player3=player3.id
WHERE
...
You could left join the players table for each player, this would work but isn't really an ideal solution. Unfortunately due to your table structure is the best you can do.
It would be much better to create a mapping table, mapping teams with players. For example:
CREATE TABLE teams
(
id INT,
PRIMARY KEY(id)
);
CREATE TABLE team_players
(
team_id INT,
player_id INT
)
This would allow you to then query by team_id and return all the players within that team easily.

Join 2 tables, print data from 2nd table when joined rows occur

The case:
I have 2 tables, 'contracts' and 'members' tied with contract.id = members.cid.
Each contract has one main member and several secondary members (usually the children and spouse of the main member). The main member's details (name, address etc) are stored in table contracts whereas, extra members details are kept in table members. (bad logic, i know but this was a mess to begin with and i need time to redesign the db and website)
The desired output:
When I run a batch print of all contracts (lets say, every Friday) I need to also print a copy of the contract for each member, too but with the member's details on the contract instead of the main member.
The question:
How does this translate into a mysql query? Ok, its a left join, but how do I say "print data from table members instead of contracts for the joined rows"?
Main fields that occur in the 2 tables are name + surname, those should be enough for a draft query example.
Example tables and data:
contracts
-------------------------
id | name | surname |
-------------------------
1 | Tom | Jones |
2 | Jamie | Oliver |
members
--------------------------------
id | cid | name | surname |
--------------------------------
1 | 1 | Jack | Jones |
2 | 1 | Anne | Jones |
3 | 2 | Cathy | Wilson |
So the results I want shoudld be:
cid | name | surname |
--------------------------
1 | Tom | Jones |
1 | Jack | Jones |
1 | Anne | Jones |
2 | Jamie | Oliver |
2 | Cathy | Wilson |
If i write
SELECT c.name as name, c.surname as surname, m.name as name, m.surname as surname
FROM contracts c
join members m on c.id = m.cid
I simply end up with
name and name_1, surname and surname_1 but I want ALL names to fall under name and likewise for all other matching columns.
Hope this works :::
select c1.id, c1.name, c1.surname
from contracts c1
union
(Select m.id, m.name, m.surname
from
members m left join contracts c on (c.id = m.cid))
This is what I finally did and it worked (field names are different but the syntax is what matters):
SELECT u.id, u.onoma_u, u.name_u,
coalesce(u.programa, aa.programa) as programa,
coalesce(u.barcode, aa.barcode) as barcode,
coalesce(u.plan, aa.plan) as plan,
coalesce(u.im_exp, aa.im_exp) as im_exp,
coalesce(u.symb, aa.symb) as symb
FROM (SELECT a1.id, a1.onoma_u, a1.name_u, a1.programa, a1.barcode, a1.plan, a1.im_exp, a1.symb
FROM aitisi a1
UNION
SELECT a2.id, m.name, m.surname, NULL, NULL, NULL, NULL, NULL
FROM members m
JOIN aitisi a2 ON a2.id = m.symbid) u
JOIN aitisi aa ON aa.id = u.id;
I used aliases and NULLS as dummy fields to fill in the blanks.