I've been struggling on the following.
I have 3 tables: players, players_clothes, and teams_clothes.
Table players:
id user team_id
1 tom 4
2 robo 5
3 bob 4
So tom and bob are both on the same team
Table players_clothes:
id clothes_id p_id
1 13 1
2 35 3
3 45 3
Bob has clothing article 35 and 45, robo has none.
Table teams_clothes:
id clothes_id team_id
1 35 4
2 45 4
3 55 4
4 65 5
This shows which teams have rights to which articles of clothing. The problem: tom is wearing an article of clothing that does no belong to his team... Let's assume this is illegal.
I'm having trouble figuring out how to capture all those who are wearing illegal clothes for a particular team.
SELECT pc.clothes_id FROM players AS p
JOIN players_clothes AS pc
ON p.id = pc.p_id
AND p.team_id = 4 GROUP BY pc.clothes_id
(I group by players_clothes.clothes_id because believe it or not, two players can be assigned the same piece of clothing)
I think this results the following set (13, 35, 45)
Now I would like to check against the actual set of clothes that team 4 owns.
SELECT clothes_id FROM teams_clothes WHERE team_id = 4 and this return (35, 45, 55)
How can I create a query so that it returns (13)? I've tried things like NOT EXISTS IN but I think the GROUP BY players_clothes.clothes_id part gets in the way
I suggest
select * from A where team_id = $team_id join B on B.a_id = A.id
where not exists
(
select 1 from C where C.clothes_id = B.clothes_id and team_id = $team_id
)
Basically, we find all As who are on their team and for each A join to all clothing they wear, and then only return the row IF we can't find indication in table C that the clothing is on our team (this covers not existent in C and exists but in the wrong team on C)
This should do the trick:
SELECT b.a_id, b.clothes_id
FROM
b INNER JOIN a
ON b.a_id = a.id
LEFT OUTER JOIN c
ON a.team_id = c.team_id
WHERE
c.clothes_id = NULL
The thought is to do an outer join on the combination of tables A/B against table C. And then only look for the cases where c.clothes_id is NULL, which would represent those cases where there is no relational match on the outer join (i.e. the clothes item is not approved for that user's team).
Not sure if this is too late for you, but I'd change the database model itself to make this situation impossible in the first place:
("Unimportant" fields omitted for brevity, including surrogate keys such as PLAYER_ID.)
Note how TEAM_ID migrates through the identifying relationship from TEAM to PLAYER, and then to the PLAYER_ARTICLE, where it merges with the same field migrated through the TEAM_ARTICLE. Since there is only one physical TEAM_ID field in the PLAYER_ARTICLE table, you can never insert a row that would reference different teams.
To put it in more abstract terms: this is a diamond-shaped dependency, where TEAM is at the top and PLAYER_ARTICLE at the bottom of the diamond. The merger at the bottom (enabled by the usage of identifying relationships) ensures sides must always point to the same top.
Your example data would be represented like this...
PLAYER:
TEAM_ID PLAYER_NO
4 1 -- Tom
5 1 -- Robo
4 2 -- Bob
TEAM_ATRICLE:
TEAM_ID ARTICLE_ID
4 35
4 45
4 55
5 65
PLAYER_ARTICLE:
TEAM_ID PLAYER_NO ATRICLE_ID
4 1 13 -- Tom: this is impossible (FK violation).
4 2 35 -- Bob
4 2 45 -- Bob
Related
I'm a bit stuck on this question and was hoping for some help. Here's where I'm at currently.
I have this TEST table of Names. Each Person can either be a recruiter or an employee. The number in Recruited_by is associated with the person_id.
Person_id Name Recruited_by
1 Jean Grayson 1
2 Paul Smith 7
3 John Do Null
4 Alex Lee 7
5 Lisa Kim 7
6 Bob Thompson 3
7 Mike Keen Null
8 Raymond Red 3
9 Alisson Jones 1
10 Kate James 3
Here is the query I have so far which I'm trying to the names of the recruiters that hire more than 3 employees (which will return nothing in this case) and the number of employees that were NOT recruited by anyone (which would be the NULL names).
SELECT T.Name as Employees, COUNT(T1.Name) as Not_hired
FROM Test AS T
WHERE COUNT(T1.Name) IS NULL
LEFT OUTER JOIN Test AS T1
ON T.Recruited_by = T1.Person_id
GROUP BY T.Name
HAVING COUNT(T1.Name) > 3
However this query is returning nothing when I should expect it to return the number of employees who were not hired by a recruiter!
If you want in the results only 1 row with 2 columns then you can do a LEFT join of the table to a query that aggregates to get the ids of the persons that hired more than 3 persons and aggregate again to get the number of persons that were not recruited by anyone:
SELECT GROUP_CONCAT(CASE WHEN t2.Recruited_by IS NOT NULL THEN t1.Name END ORDER BY t1.Name) names,
SUM(t1.Recruited_by IS NULL) total_not_recruited
FROM Test t1
LEFT JOIN (
SELECT Recruited_by
FROM Test
GROUP BY Recruited_by
HAVING COUNT(*) > 3
) t2 ON t2.Recruited_by = t1.Person_id;
You will get the names of the persons that hired more than 3 persons (if they exist) as a comma separated list.
See the demo.
I would like your opinion on which table structure to go with.
Let's say it's a table to store nba players info.
So you would have the id, name, team_id etc.
But to store the players playing position (Point Guard, Power Forward, Center etc) should I create a separate Player_Position table that connects Player_ID to Position?
Player Table:
ID Name Team_ID
-------------------------
1 LeBron James 10
2 CJ McCollum 5
Player_Position Table:
Player_ID Position
-------------------
1 PG
1 SF
1 PF
2 PG
2 SG
The other option is to have columns PG, SG, SF, PF, C as columns on the Players table so if a player plays PG and SG, those fields would be 1 and others 0.
Player Table:
ID Name Team_ID PG SG SF PF C
-------------------------------------------------
1 LeBron James 10 1 0 1 1 0
2 CJ McCollum 5 1 1 0 0 0
Player has at least 1 position, can be multiple, (can be all 5).
No new positions will be invented later, none will be removed, just those 5.
Consider this single column:
positions SET('PG', 'SG', 'SF', 'PF', 'C') NOT NULL
Think about the queries to set/change/fetch the data. Then read the docs to see the contorted way you have to use SET.
I have four tables like this:
**USERS**
___________________________
user_ID username password
---------------------------
1 user1 1234
2 user2 5678
**TEAMS**
______________________________________
team_ID formation team_name user_ID
--------------------------------------
1 4-4-2 team1 1
2 4-3-3 team2 2
**PLAYERS**
____________________________________
player_ID name position rating
------------------------------------
1 Ronaldo LW 94
2 Messi RW 93
3 Hazard LW 90
**ACTIVE PLAYERS**
___________________________________
ID player_ID team_ID cardview_ID
-----------------------------------
1 1 2 9
2 3 1 7
3 2 1 3
Each user has a team with a formation and a team name. The "active players" tables references the player_ID with the team_ID to see which players are currently active on which teams.
Let's say that user1 logs in to the application, then I want to get all the players name, ratingand their cardview_ID. Something that should look like this:
_____________________________
name rating cardview_ID
-----------------------------
Hazard 90 7
Messi 94 3
These are the players that are currently active on user1's team which is team1.
How can I get this joined table? I have tried with an inner join but that didn't seem to do the work for me.
_______________________________ EDIT_____________________________________
This is the query that doesn't give the desired result:
SELECT players.name, players.rating, activeplayers.cardview_ID
FROM players
INNER JOIN
activeplayers
ON players.player_ID = usedplayers.player_ID
I also tried to join them on team_ID.
Assuming you have the logged in user's ID available, I think this will give you what you're asking for:
SELECT
[PLAYERS].name,
[PLAYERS].rating,
[ACTIVE PLAYERS].cardview_ID
FROM [TEAMS]
JOIN [ACTIVE PLAYERS]
ON [TEAMS].team_ID = [ACTIVE PLAYERS].team_id
JOIN [PLAYERS]
ON [PLAYERS].player_id = [ACTIVE PLAYERS].player_id
WHERE [TEAMS].user_id = <logged_in_user_id>
Please also note the questions asking for clarifying details, and also feel free to respond if this query gets you part of the way but you need more information. The content in angle brackets are of course a placeholder. I also don't know your exact table names so you may need to replace what is in the square brackets with the actual table names.
Assuming that the query in your post contains a typo and is actually this:
SELECT players.name, players.rating, activeplayers.cardview_ID
FROM players
INNER JOIN
activeplayers
ON players.player_ID = activeplayers.player_ID
This query will correctly return all the players who are active players. Now to limit it only to the team for User1, you need to add an additional join to the Teams table, same way you did the join above, and then add a WHERE clause that filters on the Teams.UserID.
That's it.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 7 years ago.
Improve this question
I have three tables in my database which are listed below:
Table: teams_info
team_id team_name entry_year status
1 team 1 2015 1
2 team 2 2015 1
3 team 2 2016 1
Table: team_players
player_id team_id status members_id position
1 1 0 1 1
2 1 1 2 2
3 1 1 3 3
4 1 1 4 4
5 2 0 5 1
6 2 0 6 2
7 2 1 7 3
Table: members
members_id first_name surname gender
1 joe blogg male
2 james smith male
3 sarah marshall female
4 tony walker male
5 peter jones male
6 jessica varley female
7 jane varley female
I'm trying to get my head around how I would get the following information,
1) How many team_players from team_info.entry_year = 2015 have accepted the to join a team (this will be all players with status = 1)
2) How many team_players from team_info.entry_year = 2015 have been invited (basically counting all records that belong to 2015 teams in table team_info)
3) The total Males players from 2015 teams.
4) The total female players from 2015 teams.
I'm fairly new to SQL and it seems I have made things complicated whilst trying to follow the best standards, however I can't wrap my head around where to start.
Are you using mysql or some other RDBMS?
I'll assume mysql here, as there are a variety of join syntaxes you can use and they vary by database.
What you want to do here is do an INNER join of all 3 tables which will give you one row per intersection.
Fortunately, you have a good basic normalized structure that has separated the TEAM entity from the MEMBERS (players).
Because your app supports "seasons" you have a good many-to-many resolver table in team_players.
So, to join the tables together, strictly speaking it doesn't really matter which table you start with, but in a case like this, I will start with the many to many resolution table:
SELECT *
FROM team_players as tp LEFT JOIN teams_info as ti ON (ti.team_id = tp.team_id) LEFT JOIN members as m ON (m.members_id = tp.members_id) At this point, you are inner joining and should have 1 row essentially for every match, which is going to be one row for every row in team_players.
In your examples, you want a count, so rather than SELECT , you just want COUNT() as 'some_name'. What you alias that name to be is up to you. For example you could have:
SELECT count(*) as accepted_in_2015
FROM team_players as tp
LEFT JOIN teams_info as ti ON (ti.team_id = tp.team_id)
LEFT JOIN members as m ON (m.members_id = tp.members_id)
Of course, what is missing now is the filtration. You can add these with a WHERE clause in most cases, but I typically add them in the appropriate join criteria. So when I'm adding a WHERE/filtration criteria that has to do with a team, I will put it in the ON that involves the teams_info join.
Where it's a 'member' table criteria (status in your first question) I add that there.
So to answer your first question, this should probably work:
1) How many team_players from team_info.entry_year = 2015
have accepted the to join a team (this will be all players with status
= 1)
SELECT count(*) as accepted_in_2015
FROM team_players as tp
LEFT JOIN teams_info as ti ON (ti.team_id = tp.team_id AND ti.entry_year = 2015)
LEFT JOIN members as m ON (m.members_id = tp.members_id AND m.status = 1)
The other questions are all variations on this same blue print and can be determined the same way, however, in the case of male/female I'd probably just group by and get counts for male vs/ female in one query.
Assume I have 4 tables:
Table 1: Task
ID Task Schedule
1 Cut Grass Mon
2 Sweep Floor Fri
3 Wash Dishes Fri
Table 2: Assigned
ID TaskID (FK) PersonID (FK)
1 1 1
2 1 2
3 2 3
4 3 2
Table 3: Person
ID Name
1 Tom
2 Dick
3 Harry
Table 4: Mobile
ID PersonID (FK) CountryCode MobileNumber
1 1 1 555-555-5555
2 2 44 555-555-1234
3 3 81 555-555-5678
4 3 81 555-555-0000
I'm trying to display the
Task on a certain day
Name of person assigned to task
Phone numbers of said person
I think it should be something like the following, but I'm not sure how to set up the conditions so that the results are limited correctly:
SELECT T.ID, T.Task, P.Name, M.MobileNumber
FROM Task AS T
LEFT JOIN Assigned AS A
ON T.ID = A.TaskID
LEFT JOIN Person AS P
ON A.PersonID = P.ID
LEFT JOIN Mobile AS M
ON M.PersonID = P.ID
WHERE T.Schedule = Fri
My goal is to fetch the following information (it will be displayed differently):
Tasks Name MobileNumber
Sweep Floor, Wash Dishes Dick, Harry 44-555-555-1234, 81-555-555-5678, 81-555-555-0000
Of course, if JOIN is the wrong way to do this, please say so.
It's unclear what you want to do with duplicate data in this case, but you should be looking at using inner joins instead of outer joins, and using something like group_concat() to combine the phone numbers.