mysql select issue, multi tables - mysql

Whilst I've asked a question similar to this before, I've attempted to apply the same technique to it and its just not working as it should and erroring and various points.
I've created an sqlfiddle for this; http://sqlfiddle.com/#!2/b1a29
I'm attempting to create a select function that will return the animal_id, animal_name,animal_type_name, shelter_name, animal_type_id and location_name.
I've attempted to have a good at it with the following code but clearly I'm missing something;
$query = $this->db->query('SELECT animal_id, animal_name, animal_type_name, shelter_name, shop_id, location_name
FROM animals a
INNER JOIN shelter s ON s.shop_id = a.shop_id
INNER JOIN location l ON l.location_id = s.location_id
INNER JOIN animal_types at ON at.animal_type_id = a.animal_type_id');

The problem is that you are selecting the column name which is ambiguous or which exists on multiple tables. In this case it's the shop_id. In order to execute the query well, you need to specify where the column shop_id should come from,
SELECT animal_id, animal_name, animal_type_name,
shelter_name, s.shop_id, location_name
FROM animals a
INNER JOIN shelter s ON s.shop_id = a.shop_id
INNER JOIN location l ON l.location_id = s.location_id
INNER JOIN animal_types at ON at.animal_type_id = a.animal_type_id
SQLFiddle Demo

When I run your code, it generate error:
Column 'shop_id' in field list is ambiguous: SELECT animal_id,
animal_name, animal_type_name, shelter_name, shop_id, location_name
FROM animals a INNER JOIN shelter s ON s.shop_id = a.shop_id INNER
JOIN location l ON l.location_id = s.location_id INNER JOIN
animal_types at ON at.animal_type_id = a.animal_type_id
From the error: your shop_id column belongs to more one table, so you must qualified it with table name / table alias.
Change your query to this :
SELECT animal_id, animal_name, animal_type_name, shelter_name, s.shop_id, location_name
FROM animals a
INNER JOIN shelter s ON s.shop_id = a.shop_id
INNER JOIN location l ON l.location_id = s.location_id
INNER JOIN animal_types at ON at.animal_type_id = a.animal_type_id

Related

Is this the right way to join tables to fetch data?

I have a database with the tables:
Student(SID,Name,Surname,Age)
Registration(StudentID,CourseID)
Course(CID,Name,Cost)
I would like to extract only the name of the courses with students younger than 20. Will the query below do just that?
SELECT C.NAME
FROM Course C
INNER JOIN Registration
INNER JOIN Student S
WHERE CID = CourseID
AND SID = StudentID
AND Age < 20
GROUP BY C.NAME
I would also like to extract the number of students in each course having students younger than 20. Is it correct to do it as below?
SELECT count(S.NAME)
,C.NAME
FROM Student S
INNER JOIN Course C
INNER JOIN Registration
WHERE Age < 20
AND CID = CourseID
AND SID = StudentID
GROUP BY C.NAME
You are missing the ON part for the join otherwise it would just be a CROSS JOIN.
Your first query should look like this if you want just a distinct list of student names:
SELECT DISTINCT C.NAME
FROM Course C
INNER JOIN Registration R ON C.CID = R.CourseID
INNER JOIN Student S ON R.StudentID = S.SID
WHERE Age < 20
Your second query shouldn't really have the C.Name in the select if you want to get just a count unless you want a count of how many students have that name.
SELECT count(*)
FROM Student S
INNER JOIN Registration R ON s.SID = R.StudentID
INNER JOIN Course C ON c.CID = R.CourseID
WHERE Age < 20
GROUP BY C.NAME
First join these tables, then group by Course's PK(CID), Add the HAVING condition to filter the course which has students younger than 20.
Then use Course table to join the result to get the course name and count of students in the course.
SELECT
T1.Name,
T2.StudentCount
FROM
Course T1
INNER JOIN (
SELECT
c.CID,
COUNT(s.SID) AS StudentCount
FROM
Course c
LEFT JOIN Registration r ON c.CID = r.CourseID
LEFT JOIN Student s ON s.SID = r.StudentID
GROUP BY c.CID
HAVING COUNT(IF(s.Age < 20, 1, NULL)) > 0
) T2 ON T1.CID = T2.CID
More correctly, you should move the conditions of the join, to the join statements themselves by including them in the on clause instead of the where. While the results may not change in this instance, if you were to start including outer joins you would encounter difficulties.
SELECT count(S.NAME)
,C.NAME
FROM Student S
INNER JOIN Registration R
ON s.SID = R.StudentID
INNER JOIN Course C
ON c.CID = R.CourseID
WHERE Age < 20
GROUP BY C.NAME
There's a fiddle here showing it in action: http://sqlfiddle.com/#!9/c3b8f/1
Your first query will also produce the results you want, but again, you should move the join predicates to the join itself. Also, you don't need to perform the grouping just to get distinct values, mysql has an expression for that called distinct. So rewritten, the first query would look like:
SELECT DISTINCT C.NAME
FROM Student S
INNER JOIN Registration R
ON s.SID = R.StudentID
INNER JOIN Course C
ON c.CID = R.CourseID
WHERE Age < 20.
Again, the results are the same as what you have already but it is easier to 'read' and will put you in good stead when you move on to other queries. As it stands you have mixed implicit and explicit join syntax.
This fiddle demonstrates both queries: http://sqlfiddle.com/#!9/c3b8f/4
edit
I may have misinterpreted your original question - if you want the total number of students enrolled in a course with at least one student under 19, you can use a query like this:
select name, count(*)
from course c
inner join registration r
on c.cid = r.courseid
where exists (
select 1
from course cc
inner join registration r
on cc.cid = r.courseid
inner join student s
on s.sid = r.studentid
where cc.cid = c.cid
group by cc.cid
having min(s.age) < 20
)
group by name;
Again with the updated fiddle here: http://sqlfiddle.com/#!9/c3b8f/17

Selecting from multiple tables that use the same joins

I'm not really sure how to explain what I need to do without an example, so I hope I can explain myself well enough!
Lets say I have the following tables in my MySQL database:
buyers
sellers
adverts
addresses
locations
object_addresses
The tables buyers, sellers and adverts are all "objects". They are associated with addresses by object_addresses which has object_type, object_id and address_id.
The addresses table has a location_id to associate it with a location.
What I ultimately want is to select all types of objects that are within a certain distance (by using a latitude and longitude I have on the locations table).
I don't have a problem with the distance calculation itself. However, I am having trouble selecting all "objects" along with their respective addresses/ locations (since they all make use of object_address).
I am able to do the following:
SELECT * FROM buyers as b
INNER JOIN object_addresses as oa on oa.ObjectId = b.Id
INNER JOIN addresses as a on oa.AddressId = a.Id
INNER JOIN locations as l on a.LocationId = l.Id
WHERE oa.ObjectType = 'buyer';
I'm having a hard time incorporating sellers and adverts into the statement above.
This is likely an simple answer, but I just can't see it tonight. Can anyone point me in the right direction?
SELECT * FROM buyers as b
INNER JOIN object_addresses as oa on oa.ObjectId = b.Id
INNER JOIN addresses as a on oa.AddressId = a.Id
INNER JOIN locations as l on a.LocationId = l.Id
WHERE oa.ObjectType = 'buyer'
union
SELECT * FROM sellers as s
INNER JOIN object_addresses as oa on oa.ObjectId = s.Id
INNER JOIN addresses as a on oa.AddressId = a.Id
INNER JOIN locations as l on a.LocationId = l.Id
WHERE oa.ObjectType = 'seller'
etc?
if you don't like that - basically, you need to include the address bits multiple times - so that each one can be linked to each object type...
Try this one using join on specific conditions oa.ObjectType = 'seller',oa.ObjectType = 'advert',oa.ObjectType = 'buyer' so only related result set will join
SELECT * FROM buyers as b
INNER JOIN object_addresses as oa on (oa.ObjectId = b.Id AND oa.ObjectType = 'buyer')
INNER JOIN advert as ad on (oa.ObjectId = ad.Id AND oa.ObjectType = 'advert')
INNER JOIN seller as s on (oa.ObjectId = s.Id AND oa.ObjectType = 'seller')
INNER JOIN addresses as a on oa.AddressId = a.Id
INNER JOIN locations as l on a.LocationId = l.Id
Other solution would be join all your table and then check the object type
SELECT * FROM buyers as b
INNER JOIN object_addresses as oa on (oa.ObjectId = b.Id )
INNER JOIN advert as ad on (oa.ObjectId = ad.Id )
INNER JOIN seller as s on (oa.ObjectId = s.Id )
INNER JOIN addresses as a on oa.AddressId = a.Id
INNER JOIN locations as l on a.LocationId = l.Id
WHERE oa.ObjectType IN('buyer' , 'advert' , 'seller')

MySQL `INNER JOIN` multiples of the same table

Is it possible to INNER JOIN a MySQL query to achieve this result?
I have a table with Strategies and a table with Members. The Strategy table holds the ID of the author that corresponds to their ID in the Member table and the ID of an author that updated the existing author's work. Is it possible to grab a reference to both of these people at the same time? Something like the following, which returns no errors, but also no results...
SELECT * FROM Strategies
INNER JOIN Members AS a
INNER JOIN Members AS b
WHERE Strategies.ID='2'
AND Strategies.AuthorID = a.ID
AND Strategies.UpdateAuthorID = b.ID
Use a LEFT JOIN:
SELECT
s.*,
a.Name AS MemberName,
b.Name AS UpdatedMemberName
FROM Strategies AS s
LEFT JOIN Members AS a ON s.AuthorID = a.ID AND s.ID = 2
LEFT JOIN Members AS b ON s.UpdateAuthorID = b.ID AND s.ID = 2 ;
If you want them in one column use COALESCE:
SELECT
s.*,
COALESCE(a.Name, b.Name) AS MemberName
FROM Strategies AS s
LEFT JOIN Members AS a ON s.AuthorID = a.ID AND s.ID = 2
LEFT JOIN Members AS b ON s.UpdateAuthorID = b.ID AND s.ID = 2
SELECT toD.dom_url AS ToURL,
fromD.dom_url AS FromUrl,
rvw.*
FROM reviews AS rvw
LEFT JOIN domain AS toD
ON toD.Dom_ID = rvw.rev_dom_for
LEFT JOIN domain AS fromD
ON fromD.Dom_ID = rvw.rev_dom_from
if domain is table name

Complex MySQL query with multiple select statements

I have three tables in Mysql that are link together:
Profile (ID, Name, Stuff..)
Contact(ID, ProfileID,desc,Ord)
Address(ID,ProfileID, desc, Ord)
Now I need to select all profile from the profile table, with the “desc” field from Contact and Address where Ord = 1. (this is for a search function where in a table I’ll display the name, main contact info and main Address of a client.
I can currently do this with three separate SQL request:
SELECT Name, ID FROM Profile WHERE name=”bla”
Then in a foreach loop, I’ll run the other two requests:
SELECT ProfileID, desc FROM Contact WHERE ProfileID=MyProfileID AND Ord=1
SELECT ProfileID, desc FROM Address WHERE ProfileID=MyProfileID AND Ord=1
I know you can do multiple SELECT in one query, is there a way I could group all three SELECT into one query?
You should be able to JOIN the tables on the profile.id and the profileid in the other tables.
If you are sure the profileid exists in all three tables, then you can use an INNER JOIN. The INNER JOIN returns matching rows in all of the tables:
select p.id,
p.name,
c.desc ContactDesc,
a.desc AddressDesc
from profile p
inner join contact c
on p.id = c.profileid
inner join address a
on p.id = a.profileid
where p.name = 'bla'
and c.ord = 1
and a.ord = 1
If you are not sure that you will have matching rows, then you can use a LEFT JOIN:
select p.id,
p.name,
c.desc ContactDesc,
a.desc AddressDesc
from profile p
left join contact c
on p.id = c.profileid
and c.ord = 1
left join address a
on p.id = a.profileid
and a.ord = 1
where p.name = 'bla'
If you need help learning JOIN syntax, here is a great visual explanation of joins
This query below only selects column when an ID from Profile table has atleast one match on tables: Contact and Address. If one or both of them are nullable, use LEFT JOIN instead of INNER JOIN because LEFT JOIN displays all records from the Left-hand side table regardless if it has a match on other tables or not.
SELECT a.*,
b.desc as BDESC,
c.desc as CDESC
FROM Profile a
INNER JOIN Contact b
ON a.ID = b.ProfileID
INNER JOIN Address c
ON a.ID = c.ProfileID
WHERE b.ORD = 1 AND
c.ORD = 1 AND
a.Name = 'nameHERE'
The LEFT JOIN version:
SELECT a.*,
b.desc as BDESC,
c.desc as CDESC
FROM Profile a
INNER JOIN Contact b
ON a.ID = b.ProfileID AND b.ORD = 1
INNER JOIN Address c
ON a.ID = c.ProfileID AND c.ORD = 1
WHERE a.Name = 'nameHERE'
To further gain more knowledge about joins, kindly visit the link below:
Visual Representation of SQL Joins
i have created working demo as your requirement :
The query bellow will retrieve all matching records from the database.its retrieving profile id,name stufff and description of contact tables
select p.id,p.name,p.stauff,c.descr,a.descr from profile as p
inner join contact as c on c.profileid=p.id
inner join address as a on a.profileid=p.id
where p.name="bla" and c.ord=1 and a.ord=1

MySQL Join Query (possible two inner joins)

I currently have the following:
Table Town:
id
name
region
Table Supplier:
id
name
town_id
The below query returns the number of suppliers for each town:
SELECT t.id, t.name, count(s.id) as NumSupplier
FROM Town t
INNER JOIN Suppliers s ON s.town_id = t.id
GROUP BY t.id, t.name
I now wish to introduce another table in to the query, Supplier_vehicles. A supplier can have many vehicles:
Table Supplier_vehicles:
id
supplier_id
vehicle_id
Now, the NumSupplier field needs to return the number of suppliers for each town that have any of the given vehicle_id (IN condition):
The following query will simply bring back the suppliers that have any of the given vehicle_id:
SELECT * FROM Supplier s, Supplier_vehicles v WHERE s.id = v.supplier_id AND v.vehicle_id IN (1, 4, 6)
I need to integrate this in to the first query so that it returns the number of suppliers that have any of the given vehicle_id.
SELECT t.id, t.name, count(s.id) as NumSupplier
FROM Town t
INNER JOIN Suppliers s ON s.town_id = t.id
WHERE s.id IN (SELECT sv.supplier_id
FROM supplier_vehicles sv
WHERE sv.vehicle_id IN (1,4,6))
GROUP BY t.id, t.name
Or you could do an INNER JOIN (as your supplier join is INNER, but this will remove towns with no suppliers with those vehicles) and change the COUNT(s.id) TO COUNT(DISTINCT s.id)
If I remember correctly, you can put your second query inside the LEFT OUTER JOIN condition.
So for example, you can do something like
...
LEFT OUTER JOIN (SELECT * FROM Suppler s, Supplier_vehicles ......) s ON s.town_id=t.id
In that way you are "integrating" or combining the two queries into one. Let me know if this works.
SELECT t.name, count(s.id) as NumSupplier
FROM Town t
LEFT OUTER JOIN Suppliers s ON t.id = s.town_id
LEFT OUTER JOIN Supplier_vehicles v ON s.id = v.supplier_id
WHERE v.vehicle_id IN (1,4,6)
GROUP BY t.name