How to write SQL query using a join on 3 tables? - mysql

I have created a hypothetical scenario with some dummy data. Below are 3 basic tables and I'm looking to write a query to find what property/properties Mary has viewed.
In this scenario Mary has viewed 1 property a flat in Glasgow.
Table 1: Client
=====================
ID Name
=====================
5 Tom
6 Mary
7 John
Table 2: Property
=====================
ID CITY TYPE
=====================
14 Aberdeen House
16 Glasgow Flat
21 Glasgow House
94 London Flat
Table 3: Viewing
========================
Client Property Date
========================
5 14 01-12-2016
5 21 08-12-2016
6 16 10-10-2016

Definitely use inner joins for this, a quick example of this could be
SELECT c.Name, p.Type, p.City, v.Date
FROM Viewing v
JOIN Client c ON v.client = c.ID
JOIN Property p ON v.property = p.ID
WHERE c.Name = 'Mary'
That should show you who view what and when. I've used aliases on the table names just to keep it neat.

Related

UNION ALL not working with different columns

Left join rows and right table rows in same column
I have two tables (Manager and Worker). The name of workers under a manager should come below manager name. The expected output is presented below:-
Manager Table:
ManagerCode Name Age Location
1 Chris 52 A
2 Rick 55 B
3 David 50 C
Worker Table
ManagerCode WName Age
1 Harry 33
1 Phil 40
2 Johnny 28
2 Jeff 47
Expected table:
ManagerCode Name Location
1 Chris A
1 Harry A
1 Phil A
2 Rick B
2 Johnny B
2 Jeff B
3 David C
Union All is creating problem for Location column as number of columns become different. I could use null as Location for worker table. But there are several columns like location in Manager. Is union correct option ?
You need to join the Worker and Manager tables to get the location codes for the workers from their corresponding managers. Then you can union that with the manager table.
SELECT ManagerCode, Name, Location
FROM (
SELECT ManagerCode, Name, Location, 1 AS isManager
FROM Manager
UNION ALL
SELECT w.ManagerCode, w.Name, m.Locationm, 0 AS isManager
FROM Worker AS w
JOIN Manager AS m ON w.ManagerCode = m.ManagerCode
) AS x
ORDER BY ManagerCode, isManager DESC
The isManager column is used to order the workers after their managers.

How Do I Write a MySQL Query that Includes a row that Has Null Data?

Below is a description of the scenario I'm trying to solve.
I have two tables. One table called displays is shown below.
id(INT) name(VARCHAR) start_date(DATE) end_Date(DATE) person_id(INT)
5 Whales 2019-01-01 2019-05-31 7
19 Universe 2019-02-01 2019-03-31 13
25 Grizzlies 2019-03-01 2019-06-30 7
46 Homosapiens? 2019-07-01 2019-09-31 13
The other table called people is shown below.
id(INT) name(VARCHAR) background(TEXT)
7 Ron Swanson Nonconformist
13 Tom Haverford Hustler
71 Andy Dwyer Goofball
I'm looking to write a SQL query that finds all the names of all displays and the names of the people associated with the displays. If a display does not have a person, the display should not be in the result. Even if a person does not have a display, the person's name should be present in the final result. The correct query should show the following results (see table below).
Ron Swanson Whales
Tom Haverford Universe
Ron Swanson Grizzlies
Tom Haverford Homosapiens?
Andy Dwyer
The query I've come up with is
SELECT p.name, d.name from displays d, people p WHERE d.person_id = p.id;
Which gets the right result minus Andy Dwyer. I'm stumped on how to include Andy Dwyer, what am I missing?
You need to use a LEFT JOIN from people to displays:
SELECT p.name, d.name
FROM people p
LEFT JOIN displays d ON d.person_id = p.id
Output (for your sample data)
name name
Ron Swanson Whales
Tom Haverford Universe
Ron Swanson Grizzlies
Tom Haverford Homosapiens?
Andy Dwyer (null)
Demo on SQLFiddle

MySQL query table filtering issue

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

Grouping Sql Tables

I have three tables which i have joined ..but the manner in which i have to display them is a little difficult for me to understand.I am trying to group them and not getting the desired result.
Sports_Name
Id | Name
---------
1 Football
2 Cricket
3 Hockey
4 Tenis
Teams
Id | Sport_Id | Team_Name
----------------------------
101 1 Manchester United
103 2 Australia
104 2 India
109 1 Real Madrid
110 3 New Zeland
Player_Name
Id | Team_Id | Player_Name
------------------------------
1 101 Rooney
2 104 Tendulkar
3 103 Ponting
4 109 Ronaldo
5 101 Van Persie
6 110 Simond
I need to display this information in the following manner..
**Football**
Manchester United -Rooney
Manchester United -Van Persie
Real Madrid -Ronaldo
**Cricket**
India-Tendulkar
Australia-Ponting
**Hockey**
New Zeland -Simond
and something similar for tenis
See this picture for details
Is this what you want?
SELECT sports_name.name, teams.team_name, player_name.player_name FROM player_name
JOIN
teams ON
player_name.team_ID = teams.ID
JOIN
sports_name on
sports_name.id = teams.sport_id
order by sports_name.name, team_name
This gets all the data in order then you just need to use your presentation layer to use sports.name as group header.
This produces
name team_name player_name
Cricket Australia Ponting
Cricket india Tendulkar
Football Man u Rooney
Football Man u RVP
Football Real Madrid Ronaldo
Hockey N Z Simond
try this:
SELECT T.TEAM_NAME+' - '+ P.PLAYER_NAME as 'FOOTBALL'
FROM TEAMS T JOIN PLAYER_NAME P
ON T.ID=P.TEAM_ID
JOIN SPORTS_NAME S
ON T.SPORT_ID=S.ID
WHERE S.NAME='FOOTBALL'
Change the where clause to 'Cricket','Hockey' to get other results
The basic query would be something like:
SELECT s.Name, t.Name, p.Player_Name
FROM Player_Name p
INNER JOIN Teams t
ON t.Id = p.Team_id
INNER JOIN Sports_Name s
ON s.Id = t.Sport_Id
Then if you want to display the results like you described it, my guess would be to use a cursor. Use parameterized procedure to add a WHERE clause to the query in order to filter the results by sport's name.

MySQL Join Multiple (More than 2) Tables with Conditions

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.