How to link 4 tables using an mySQL query - mysql

Vehicle
+--------------------+--------------+------+-----+---------+
| Field | Type | Null | Key | Default |
+--------------------+--------------+------+-----+---------+
| id | int(11) | NO | Pk | NULL |
| model | varchar(35) | NO | | NULL |
+--------------------+--------------+------+-----+---------+
info
+--------------------+--------------+------+-----+---------+
| Field | Type | Null | Key | Default |
+--------------------+--------------+------+-----+---------+
| id | int(11) | NO | Pk | NULL |
| vehicle_id | varchar(35) | NO | FK | NULL |
| location | varchar(35) | NO | | NULL |
+--------------------+--------------+------+-----+---------+
Axle 1
+--------------------+--------------+------+-----+---------+
| Field | Type | Null | Key | Default |
+--------------------+--------------+------+-----+---------+
| id | int(11) | NO | Pk | NULL |
| vehicle_id | varchar(35) | NO | FK | NULL |
| weight | varchar(35) | NO | | NULL |
+--------------------+--------------+------+-----+---------+
Axle 2
+--------------------+--------------+------+-----+---------+
| Field | Type | Null | Key | Default |
+--------------------+--------------+------+-----+---------+
| id | int(11) | NO | Pk | NULL |
| vehicle_id | varchar(35) | NO | FK | NULL |
| weight | varchar(35) | NO | | NULL |
+--------------------+--------------+------+-----+---------+
I wish to create a query that will return all the fields from all tables with a common vehicle_id. The vehicle_id being a reference in each of the 3 tables (info, axle1, axle 2) to the primary key in the Vehicle table. Could someone please explain how I might go about doing so? I tried using multiple joins but it didnt work!Many thanks.
EDIT:
Query I tried was;
SELECT *
FROM Vehicle
JOIN info, axle1, axle 2
ON vehicle.id = axle1.vehicle_id
AND vehicle.id = axle2.vehicle_id AND vehicle.id = info.vehicle_id

Try this instead:
SELECT *
FROM Vehicle v
INNER JOIN Info i ON v.id = i.vehicle_id
INNER JOIN Axle1 a1 ON i.vehicle_id = a1.vehicle_id
INNER JOIN Axle2 a2 ON a1.vehicle_id = a2.vehicle_id

Check this link: mySQL get information from multiple tables in one query I think there's an example that may fit
You just need to specify an identifier for each table, so you can specify which table are you talking about when you refer to a field. Then, make sure you link all the id's as a condition so you leave out all the combinations where identifiers don't match. That would be:
SELECT * FROM Vehicle v, Info i, Axle1 a1, Axle2 a2 WHERE v.id == a1.vehicle_id AND v.id == a2.vehicle_id AND v.id == i.vehicle_id

Related

SQL, Select multiple values based on foreign key, return the values

How to select each team member name in a query, pilot and copilot based on the two foreign keys in team table
Table: player
+----------------+-----------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------------+------------------------+-----+---------+----------------+
| id | int(10) unsigned| NO | PRI | NULL | auto_increment |
| name | varchar(60) | NO | | NULL | |
| isPilot | TINYINT(1) | NO | | NULL | |
| age | int(4) | NO | | NULL | |
+----------------+-----------------+------+-----+---------+----------------+
table: team
+----------------+-----------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------------+-----------------+------+-----+---------+----------------+
| id | int(10) unsigned| NO | PRI | NULL | auto_increment |
| pilot_id | int(10) unsigned| NO | FK | NULL | |
| copilot_id | int(10) unsigned| NO | FK | NULL | |
+----------------+-----------------+------+-----+---------+----------------+
Is this a good database setup?
Might be a repost of SQL: Foreign Key With multiple child values , but can't apply it to mine
MCVE: https://www.db-fiddle.com/f/a2fCdy6RFqgReuL8FThhDP/2
Result should be something like
+----------------+-----------------+
| Pilot | Co-Pilot |
+----------------+-----------------+
| player4Name | player3Name |
| player2Name | player1Name |
+----------------+-----------------+
Try this :
select p1.name, p2.name from player p1
join team t1 on p1.id = t1.pilot_id
join player p2 on p2.id = t1.copilot_id;
It seems like you need two simple JOINs :
SELECT
p.name AS pilot,
cp.name AS copilot
FROM
team AS t
INNER JOIN player AS p on p.id = t.pilot_id
INNER JOIN player AS cp on cp.id = t.copilot_id

Getting a SQL query to print 0 for null count results across 3 tables

I'm trying to get a SQL query to give me the results of a count but I need the result to include rows where the count is 0. What I found for solutions to this was to use IFNULL(COUNT(*), 0) in place of COUNT(*) however that had no effect on the result. I also tried using a LEFT JOIN but SQL gave me a syntax error if I tried to put in those. Here's my table setup
User
+-------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+--------------+------+-----+---------+----------------+
| UserID | mediumint(9) | NO | PRI | NULL | auto_increment |
| firstName | varchar(15) | NO | | NULL | |
| lastName | varchar(15) | NO | | NULL | |
| Protocol | varchar(10) | NO | | NULL | |
| Endpoint | varchar(50) | NO | | NULL | |
| UsergroupID | mediumint(9) | NO | MUL | NULL | |
+-------------+--------------+------+-----+---------+----------------+
Subscription
+----------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------------+--------------+------+-----+---------+----------------+
| SubscriptionID | mediumint(9) | NO | PRI | NULL | auto_increment |
| TopicID | mediumint(9) | NO | MUL | NULL | |
| UserID | mediumint(9) | NO | MUL | NULL | |
+----------------+--------------+------+-----+---------+----------------+
Topic
+----------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+--------------+------+-----+---------+----------------+
| TopicID | mediumint(9) | NO | PRI | NULL | auto_increment |
| Name | varchar(50) | NO | | NULL | |
| FBName | varchar(30) | YES | | NULL | |
| FBToken | varchar(255) | YES | | NULL | |
| TWName | varchar(10) | YES | | NULL | |
| TWToken | varchar(50) | YES | | NULL | |
| TWSecret | varchar(50) | YES | | NULL | |
+----------+--------------+------+-----+---------+----------------+
My SQL query to try and get the COUNT is :
SELECT Topic.TopicID as ID, Topic.Name AS TopicName, COUNT(*) AS numSubscriptions
FROM User, Topic, Subscription
WHERE Subscription.UserID = User.UserID
AND Subscription.TopicID = Topic.TopicID
GROUP BY Topic.TopicID;
I've tried replacing COUNT(*) with IFNULL(COUNT(*), 0) and I've tried to replace User,Topic,Subscription with User JOIN Subscription JOIN Topic and I also tried User LEFT JOIN Subscription LEFT JOIN Topic but that got a SQL error.
The output I'm getting is:
+----+-----------+------------------+
| ID | TopicName | numSubscriptions |
+----+-----------+------------------+
| 2 | test | 2 |
| 3 | test2 | 1 |
+----+-----------+------------------+
I need to be getting
+----+-----------+------------------+
| ID | TopicName | numSubscriptions |
+----+-----------+------------------+
| 2 | test | 2 |
| 3 | test2 | 1 |
| 4 | test3 | 0 |
+----+-----------+------------------+
By default, outer joins are left to right. So, the trick is to start with Topic:
SELECT Topic.TopicID as ID, Topic.Name AS TopicName,
COUNT(User.UserID) AS numSubscriptions
FROM Topic
LEFT JOIN Subscription
ON Subscription.TopicID = Topic.TopicID
JOIN User
ON User.UserID = Subscription.UserID
GROUP BY Topic.TopicID
This allows for multiple subscriptions per user and requires that the user record exists to be considered in the count.
COUNT(NULL) evaluates to 0, so any topic records without a corresponding subscription and user record will show as 0.
If you're not concerned whether the user record exists, you could simplify it to the following:
SELECT Topic.TopicID as ID, Topic.Name AS TopicName,
COUNT(Subscription.TopicID) AS numSubscriptions
FROM Topic
LEFT JOIN Subscription
ON Subscription.TopicID = Topic.TopicID
GROUP BY Topic.TopicID
The example below should do what you're after. The column in the COUNT() can be any column of the subscription table, but using its ID is a good practice.
Using the left join ensures that all entries of the user table will show up in the results, even if there are no matching subscriptions.
SELECT User.firstName,
User.lastName,
Topic.Name AS TopicName,
COUNT(Subscription.SubscriptionId) AS numSubscriptions
FROM USER
LEFT OUTER JOIN Subscription ON Subscription.UserID=USER.UserID
LEFT OUTER JOIN Topic ON Subscription.TopicID=Topic.TopicID
GROUP BY User.firstName, User.lastName, Topic.Name;

MySQL joining data from 2 tables based on foreign key in a third, without duplicates or group by

I have three tables that look like this:
People:
+------------+-------------+------+-----+-------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+-------------+------+-----+-------------------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| fname | varchar(32) | NO | | NULL | |
| lname | varchar(32) | NO | | NULL | |
| dob | date | NO | | 0000-00-00 | |
| license_no | varchar(24) | NO | | NULL | |
| date_added | timestamp | NO | | CURRENT_TIMESTAMP | |
| status | varchar(8) | NO | | Allow | |
+------------+-------------+------+-----+-------------------+----------------+
Units:
+----------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| number | varchar(3) | NO | | NULL | |
| resident | int(11) | NO | MUL | NULL | |
| type | varchar(16) | NO | | NULL | |
+----------+-------------+------+-----+---------+----------------+
Visits:
+----------+-----------+------+-----+---------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+-----------+------+-----+---------------------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| vis_id | int(11) | NO | MUL | NULL | |
| unit | int(11) | NO | MUL | NULL | |
| time_in | timestamp | NO | | CURRENT_TIMESTAMP | |
| time_out | timestamp | NO | | 0000-00-00 00:00:00 | |
+----------+-----------+------+-----+---------------------+----------------+
There are multiple foreign keys linking these tables:
units.resident -> people.id
visits.unit -> units.id
visits.vis_id -> people.id
I am able to run this query to find all residents ie - everyone from people that are referenced by the units.resident foreign key:
SELECT concat(p.lname, ', ', p.fname) as 'Resident', p.dob as 'Birthday',
u.number as 'Unit #'
from people p, units u
where p.id = u.resident
order by u.number
It returns the results I want... However, it'd be useful to do the opposite of this to find all the people who are not residents ie- everyone from people who aren't referenced by the units.resident foreign key.
I've tried many different queries, most notably some inner and left joins, but I'm getting waaaaay too many duplicate entries (from what I've read here, this is normal). The only thing I've found that works is using a group by license_no, because as of now the "residents" don't have this information, like this:
SELECT p.id, concat(p.lname, ', ', p.fname) as 'Visitor',
p.license_no as 'License', u.number from people p
left join units u on u.number <> p.id
group by p.license_no order by p.id;
This works for all but one resident, who's u.number is displayed on ALL results. The residents will soon have license_no entries, and I can't have that one odd entry in the returned results all the time, so this query won't work as a long-term solution.
How can I structure a query without a group by that will return the results I want?
This should work
SELECT
p.id
, P.fname
, P.lname
FROM
people AS p
LEFT JOIN
units AS u
ON
p.id = u.resident
WHERE
u.resident IS NULL
Extra hint.
Table people should be called person.
By u.resident you mean a person. so it should be a person_id there in the unit table...
Better logic helps to write SQL better, if your name convention is clear to use.
Use a NOT EXISTS clause to exclude those people who are residents.
SELECT P.id
,P.fname
,P.lname
,etc...
FROM People P
WHERE NOT EXISTS (SELECT 1 FROM Units U WHERE U.resident = P.id)

MySQL join four tables and get some kind of SUM result

I have four tables like this:
mysql> describe courses;
+-----------------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------------+-------------+------+-----+---------+----------------+
| course_id | int(11) | NO | PRI | NULL | auto_increment |
| course_name | varchar(75) | YES | | NULL | |
| course_price_id | int(11) | YES | MUL | NULL | |
+-----------------+-------------+------+-----+---------+----------------+
mysql> describe pricegroups;
+-------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+--------------+------+-----+---------+----------------+
| price_id | int(11) | NO | PRI | NULL | auto_increment |
| price_name | varchar(255) | YES | | NULL | |
| price_value | int(11) | YES | | NULL | |
+-------------+--------------+------+-----+---------+----------------+
mysql> describe courseplans;
+------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+----------------+
| plan_id | int(11) | NO | PRI | NULL | auto_increment |
| plan_name | varchar(255) | YES | | NULL | |
| plan_time | int(11) | YES | | NULL | |
+------------+--------------+------+-----+---------+----------------+
mysql> describe course_to_plan;
+-----------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------+---------+------+-----+---------+-------+
| course_id | int(11) | NO | PRI | NULL | |
| plan_id | int(11) | NO | PRI | NULL | |
+-----------+---------+------+-----+---------+-------+
Let me try to explain what I have and what I would like to do...
All my courses (course_id) has different steps (plan_id) wich has a value of 1 or more days (plan_time). A course has one or more steps (course_to_plan)A course is connected to a pricegroup (price_id).
I would like to query my MySQL database and get an output off:
The course_name, the plan_id's it has, and based on the value of price_id together with the value in the plan_time get a result who looks something like this:
+------------+--------------+------------+---------+
| course_name| pricegroup | plan_time | RESULT |
+------------+--------------+------------+---------+
| Math | Expensive | 7 | 3500 |
+------------+--------------+------------+---------+
I hope you understand me...
Is it even possible with the structure I have or should I "rebuild-and-redo-correct" something?
SELECT c.course_name, p.price_name, SUM(cp.plan_time), SUM(cp.plan_time * p.price_value)
FROM courses c
INNER JOIN pricegroups p ON p.price_id = c.course_price_id
INNER JOIN course_to_plan cpl ON cpl.course_id = c.course_id
INNER JOIN courseplans cp ON cp.plan_id = cpl.plan_id
GROUP BY c.course_name, p.price_name
Please note that it seems to me that your implementation might be erroneous. The way you want the data makes me think that you could be happier with a plan having a price, so you don't apply the same price for a plan which is "expensive" AND another plan which is "cheap", which is what you are doing at the moment. But I don't really know, this is intuitive :-)
Thanks for accepting the answer, regards.
Let me see if I understand what you need:
SELECT c.course_name, pg.price_name,
COUNT(cp.plan_time), SUM(pg.price_value * cp.plan_time) AS result
FROM courses c
INNER JOIN pricegroups pg ON c.course_price_id = pg.price_id
INNER JOIN course_to_plan ctp ON c.course_id = ctp.course_id
INNER JOIN courseplans cp ON ctp.plan_id = cp.plan_id
GROUP BY c.couse_name, pg.price_name

MySQL Left join WHERE table2.field = "X"

I have the following tables:
pages:
+------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+----------------+
| page_id | int(11) | NO | PRI | NULL | auto_increment |
| type | varchar(20) | NO | | NULL | |
| categories | varchar(255) | NO | | NULL | |
| title | varchar(255) | NO | MUL | NULL | |
| text | longtext | NO | MUL | NULL | |
+------------+--------------+------+-----+---------+----------------+
custom:
+---------+------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+------------------+------+-----+---------+-------+
| page_id | int(10) unsigned | NO | PRI | NULL | |
| key | varchar(255) | NO | PRI | NULL | |
| value | longtext | NO | | NULL | |
+---------+------------------+------+-----+---------+-------+
I want to join the tables in a way where:
1) all the entries from the first table are returned LEFT JOIN custom ON pages.page_id = custom.page_id
2) pages.type IN ('type_a', 'type_b', 'type_c')
3) "key" from the second table has value "votes" custom.key = 'votes'
I made everything so far, but the third condition is the problem. If there isn't entry for key = 'votes' in table custom the query returns only these with entries. I want to return NULL if missing entries.
I need key = 'votes', because I have other entries for this page_id where the key is not 'votes' and this duplicates the rows from pages
Simply add your contraint custom.key='votes' to the LEFT JOIN
SELECT *
FROM pages LEFT JOIN custom
ON pages.page_id=custom.page_id AND custom.key='votes'
WHERE pages.type IN('type_a','type_b','type_c') ;
I'd do it like this:
SELECT *
FROM pages
LEFT JOIN
( SELECT * From custom where key='votes') cv
on pages.page_id = cv.page_id
WHERE pages.type IN ('type_a', 'type_b', 'type_c');
try changing your where condition to custom.key = 'votes' OR custom.key is null.