Loading details from multiple tables - php

I was using this:
SELECT res.*, rac.*, u.*, t.*, c.*
FROM Results res
INNER JOIN Races rac USING (RaceID)
INNER JOIN Users u USING (UserID)
INNER JOIN Teams t USING (TeamID)
INNER JOIN Cars c USING (CarID)
WHERE res.SeasonNumber = '$SeasonNumber' AND res.LeagueID = '$LeagueID' AND Position = '1' AND ResultConfirmed = '1'
ORDER BY Position ASC
Which works fine, but I've since realised I need to have CarID added in to Results table, but when I add it in, it gives me the error that the field is ambiguous. What I'd like to do is get the Car name from Cars table where CarID joins Cars and Results. When I try to do this though:
SELECT res.*, rac.*, u.*, t.*, c.*
FROM Results res
INNER JOIN Races rac USING (RaceID)
INNER JOIN Users u USING (UserID)
INNER JOIN Teams t USING (TeamID)
INNER JOIN Cars c USING (res.CarID)
WHERE res.SeasonNumber = '$SeasonNumber' AND res.LeagueID = '$LeagueID' AND Position = '1' AND ResultConfirmed = '1'
ORDER BY Position ASC
I get the following error:
You have an error in your SQL syntax; check the manual that
corresponds to your MySQL server version for the right syntax to use
near '.CarID) WHERE res.SeasonNumber = '1' AND res.LeagueID = '1' AND
Position = '1' ' at line 6

You can replace your USING clause with ON(),in USING() clause i guess you add the columns name that are same in other table you are joining but you placed the join in last and using alias res mysql won't allow this
INNER JOIN Cars c ON(res.CarID =c.CarID)
If you need to use USING() clause you need to adjust the join placements like
SELECT res.*, rac.*, u.*, t.*, c.*
FROM
Cars c
INNER JOIN Results res USING (CarID)
INNER JOIN Races rac USING (RaceID)
INNER JOIN Users u USING (UserID)
INNER JOIN Teams t USING (TeamID)
WHERE res.SeasonNumber = '$SeasonNumber' AND res.LeagueID = '$LeagueID' AND Position = '1' AND ResultConfirmed = '1'
ORDER BY Position ASC
But ON() clause is more readable form

Related

MYSQL/PHP: Concat returning to many fields on LEFT JOIN

I had a SELECT query with a LEFT JOIN working as desired. I then added one more table via a smilar LEFT JOIN and now I am getting a wierd result. Basically, for a group_concat where I was getting one item for every record, I am getting eight records. I don't see why this is happening because the join to the new table is analagous to several other joins that do not have this problem (that I have omitted from the example for clarity).
Here is the query that is fine:
$sql = "SELECT t.*,
group_concat(tf.todoid) as `tftodoid`,
group_concat(tf.id) as `tfid`,
group_concat(tf.filedescript) as `tffiledescript`,
group_concat(tf.filename) as `tffilename`,
group_concat(tf.founderid) as `tffounderid`,
group_concat(tf.ext) as `tfext`,
group_concat(tf.lasttouched) as `tilt`
FROM titles `t`
LEFT JOIN titlefiles `tf`
ON (tf.todoid = t.id AND tf.founderid = '$userid')
WHERE t.userid='$userid'
GROUP BY t.id";
And here is the query with the extra join that is now spilling out the multiple copies of the items:
$sql = "SELECT t.*,
group_concat(tf.todoid) as `tftodoid`,
group_concat(tf.id) as `tfid`,
group_concat(tf.filedescript) as `tffiledescript`,
group_concat(tf.filename) as `tffilename`,
group_concat(tf.founderid) as `tffounderid`,
group_concat(tf.ext) as `tfext`,
group_concat(tf.lasttouched) as `tilt`,
group_concat(s.id) as `stepid`,
group_concat(s.step) as `steps`
FROM titles `t`
LEFT JOIN titlefiles `tf`
ON (tf.titleid = t.id AND tf.founderid = '$userid')
LEFT JOIN steps `s`
ON s.titleid = t.id
WHERE t.userid='$userid'
GROUP BY t.id";
Here is an example of output in JSON showing the difference:
First query:
"tfid":"56,57,58,59,60,61,62,63,64,65,66,67,68,75,76,81"
Second query:
"tfid":"56,57,58,59,60,61,62,63,64,65,66,67,68,75,76,81,56,57,58,59,60,61,62,63,64,65,66,67,68,75,76,81,56,57,58,59,60,61,62,63,64,65,66,67,68,75,76,81,56,57,58,59,60,61,62,63,64,65,66,67,68,75,76,81,56,57,58,59,60,61,62,63,64,65,66,67,68,75,76,81,56,57,58,59,60,61,62,63,64,65,66,67,68,75,76,81,56,57,58,59,60,61,62,63,64,65,66,67,68,75,76,81,56,57,58,59,60,61,62,63,64,65,66,67,68,75,76,81,56,57,58,59,60,61,62,63,64,65,66,67,68,75,76,81",
I suspect the problem has something to do with the JOIN or with the Group By statements but I can't see how to fix.
How can I ensure that I get only one fileid per file as opposed to eight?
Alter the line as follows:
group_concat(DISTINCT tf.id) as `tfid`,
This then only gets you the unique ids.
If you want them ordered add:
group_concat(DISTINCT tf.id ORDER BY tf.id ASC) as `tfid`,

Mysql left join issue with 3 tables not returning all rows from first table

I have below query
select catid, cat_name, currency, count(is_reporting_category_sales.id) as total_sales,
sum(total_sales) as total_earning
from is_category
left join is_reporting_category_sales on is_category.catid = is_reporting_category_sales.category_id
join is_reporting_order on is_reporting_order.id = is_reporting_category_sales.order_id
group by catid, cat_name, currency
ORDER BY `is_category`.`cat_name` ASC
but this is returning only rows that are common in is_category and is_reporting_category_sales, is_reporting_order but I want to fetch all rows from is_category table. And if there is no order for the category then 0 as total_earning and total_sales.
You have to Use Left Join
left join is_reporting_order on is_reporting_order.id = is_reporting_category_sales.order_id
Instead of
join is_reporting_order on is_reporting_order.id = is_reporting_category_sales.order_id
Perhaps using left outer joins you might get the results you expect ( had to guess at some of the aliases for columns btw so some of them might be wrong )
select c.`catid`, c.`cat_name`, `currency`, count(i.`id`) as 'total_sales', sum(`total_sales`) as 'total_earning'
from `is_category` c
left outer join `is_reporting_category_sales` i on c.`catid` = i.`category_id`
left outer join `is_reporting_order` on o.`id` = i.`order_id`
group by c.`catid`, c.`cat_name`, `currency`
order by c.`cat_name` asc;

Left Join with subquery returns Null

I am applying Left Join with Sub query & where clause
It seems fine no syntax error but the columns I am selecting from that sub query always returns me Null. I have executed the same part in my SQL section, it gives me record. Kindly have a look on the query and let me know if any thing is possible or if question is not clear.
SELECT alt.userId, u.name, t.name AS teamName, alt.startDateTime, v.name AS villageName, c.name AS clusterName, startLat, startLong, latlng.lat, latlng.long
FROM activity_log_tim AS alt
JOIN user AS u ON u.userId = alt.userId
JOIN team_members AS tm ON tm.memberId = u.userId
JOIN team AS t ON t.teamId = tm.teamId
JOIN village AS v ON v.villageId = alt.villageId
JOIN cluster_villages AS cv ON cv.villageId = v.villageId
JOIN cluster AS c ON c.clusterId = cv.clusterId
LEFT JOIN (SELECT lat, long,dateTime, scheduleId FROM activity_log_gps LIMIT 1) AS latlng ON latlng.scheduleId = alt.scheduleId
WHERE DATE(alt.startDateTime) = '2015-09-05' AND DATE(alt.endDateTime) = '0000-00-00' GROUP BY alt.userId ORDER BY latlng.dateTime DESC
Well it's supposed to since you are performing a LEFT JOIN which produces NULL if no match occurs. So either you can perform a INNER JOIN or use a COALESCE function like below
SELECT alt.userId, u.name,
t.name AS teamName,
alt.startDateTime,
v.name AS
villageName,
c.name AS clusterName,
startLat,
startLong,
COALESCE(latlng.lat, 23), //Notice the use of COALESCE. If null will return 23 as default
COALESCE(latlng.long, 32)

Trying to JOIN an empty table nothing returns

I have a problem trying to JOIN an empty table (comments table) to my existing prepared statement.
This is working perfectly:
// prepare images
if ($stmt = $mysqli->prepare(" SELECT uu.*, m.*,
(
SELECT COUNT(*)
FROM img_likes AS t
WHERE t.img_id = uu.imgID AND t.user_id = ?
) AS user_likes,
(
SELECT COUNT(*)
FROM img_likes AS t
WHERE t.img_id = uu.imgID
) AS total_likes
FROM user_uploads AS uu
INNER JOIN members AS m ON m.id = uu.user_id
ORDER BY up_time DESC")) {
$stmt->bind_param('i', $user_id);
$stmt->execute(); // get imgs
// foreach print images
// working as expected
}
And I don't know why if I JOIN another table (img_comments) that is empty, the images are not printed... if I add a row to the table and refresh the page, one image is printed...
The statement that I'm trying and it's not working is this:
SELECT uu.*, m.*, ic.*,
(
SELECT COUNT(*)
FROM img_likes AS t
WHERE t.img_id = uu.imgID AND t.user_id = ?
) AS user_likes,
(
SELECT COUNT(*)
FROM img_likes AS t
WHERE t.img_id = uu.imgID
) AS total_likes
FROM user_uploads AS uu
INNER JOIN members AS m ON m.id = uu.user_id
INNER JOIN img_comments AS ic ON ic.img_id = uu.imgID
ORDER BY up_time DESC
Why is only printing images based on the number of the table rows?? I also tried LEFT JOIN but I'm not too familiareize with this. I only use INNER JOIN in other scripts and I never had a problem like this.
I would appreciate any optimization to my query.
What does an inner join do? It joins all records of table a with all matching records of table b. So when there are no records in table b, there is no match for any record of table a, hence no result at all. Why does this surprise you?
A left join is an outer join (short for LEFT OUTER JOIN). It means: Give me all records of table a with all matching records of table b, and when there is no match then give me the record of table a anyhow. This seems to be what you are wanting here. But you say you tried it. I don't see how this would fail in your query.
A typical error for an outer join not to work would be to have some field of b in your where clause (e.g. where b.id > 100). As the outer-joined records have no matching b record, all b fields are null, so that such a where clause would fail. You'd just get matches again, just like with the inner join.
EDIT: As to optimization, you can get the two counts in one pass by counting conditionally:
SELECT
uu.*, m.*, ic.*,
il.count_user AS user_likes,
il.count_total AS total_likes
FROM user_uploads AS uu
INNER JOIN members AS m ON m.id = uu.user_id
LEFT OUTER JOIN img_comments AS ic ON ic.img_id = uu.imgID
LEFT OUTER JOIN
(
select
img_id,
count(*) as count_total,
count(case when t.user_id = ? then 1 end) as count_user
from img_likes
group by img_id
) AS il ON il.img_id = uu.imgID
ORDER BY uu.up_time DESC;
As far as I know, INNER JOIN will only retrieve data which have both data. So if let say the table that you join have no data with that join condition. It will not return any data at all.
LEFT JOIN is just a normal join. It will retrieve data on both table. But if the joined table is empty, then only the primary table will have data, the secondary table will have null as its data.
You can just modify your code, replacing INNER JOIN with LEFT JOIN and see if it works/

mysql join return 0 for one column with other columns intact

I wish to join multiple tables like- Categories, menus, restaurants, reviews, etc.
to return the restaurants that provide the inserted food with their prices.
Everything works except numberOfReviews in reviews table.
If a restaurant has no reviews then output should be 0 for numOfReviews column but other column values should be retrieved i.e. price, name, etc.
With following query I get all fields as null and count(numReviews) as 0:
select r.id
,r.`Name`
,r.`Address`
,r.city
,r.`Rating`
,r.`Latitude`
,a.`AreaName`
,m.`Price`
,count(rv.id)
from `categories` c, `menus` m, `restaurants` r, areas a, reviews rv
where m.`ItemName`="tiramisu"
and c.`restaurant_id`=r.`id`
and m.`category_id`=c.id
and r.`AreaId`=a.`AreaId`
and if I can't match rv.restaurant_id=r.id in where clause(obviously).
Where am I getting wrong? How do I solve this?
edited
select r.id,
r.`Name`,
r.`Address`,
r.city,
r.`Rating`,
r.`Latitude`,
a.`AreaName`,
m.`Price`,
r.`Longitude`,
r.Veg_NonVeg,
count(rv.id)
from restaurants r LEFT JOIN `reviews` rv on rv.`restaurant_id`=r.`id`
inner join `categories` c on c.`restaurant_id` = r.id
inner join `menus` m on m.`category_id` = c.id
inner join `areas` a on a.`AreaId` = r.`AreaId`
where m.`ItemName`="tiramisu"
First of all, don't use this old school syntax for the jointures.
Here is a query that may solve your problem:
SELECT R.id
,R.Name
,R.Address
,R.city
,R.Rating
,R.Latitude
,R.Longitude
,A.AreaName
,M.Price
,R.Veg_NonVeg
,COUNT(RV.id) AS numOfReviews
FROM restaurants R
INNER JOIN categories C ON C.restaurant_id = R.id
INNER JOIN menus M ON M.category_id = C.id
INNER JOIN areas A ON A.AreaId = R.AreaId
LEFT JOIN reviews RV ON RV.restaurant_id = R.id
WHERE M.ItemName = 'tiramisu'
GROUP BY R.id, R.Name, R.Address, R.city, R.Rating, R.Latitude, R.Longitude, A.AreaName, M.Price, R.Veg_NonVeg
I used explicit INNER JOIN syntax instead of your old school syntax and I modified the jointure with table reviews in order to get the expected result. The GROUP BY clause is required to use the aggregate function COUNT, every rows will be grouped by the enumerated columns (every column except the one used by the function).
Here is another solution that simplify the GROUP BY clause and allow the modification of SELECT statement without having to worry about the fact that every columns need to be part of the GROUP BY clause:
SELECT R.id
,R.Name
,R.Address
,R.city
,R.Rating
,R.Latitude
,R.Longitude
,A.AreaName
,M.Price
,R.Veg_NonVeg
,NR.numOfReviews
FROM restaurants R
INNER JOIN (SELECT R2.id
,COUNT(RV.id) AS numOfReviews
FROM restaurants R2
LEFT OUTER JOIN reviews RV ON RV.restaurant_id = R2.id
GROUP BY R2.id) NR ON NR.id = R.id
INNER JOIN categories C ON C.restaurant_id = R.id
INNER JOIN menus M ON M.category_id = C.id
INNER JOIN areas A ON A.AreaId = R.AreaId
WHERE M.ItemName = 'tiramisu'
As you can see here I added a new jointure on a simple subquery that does the aggregation job in order to provide me the expected number of reviews for each restaurant.
Hope this will help you.

Categories