Condition with left join table in mysql - php

I have Quiz table which is shown in image.
and quiz result history table like
I write left join query between these two table.
My query is like:
select q.*
, c.name as catname
, qrh.q_complete
, q s.test_type
, qs.questionid
, sum(qrh.score) as score1
, sum(qrh.total_questions) as tot_que
from categories c
, quiz q
left
join user_quiz_results_history qrh
on qrh.quiz_id = q.quizid
left
join quizquestions qq
on qq.quizid = q.quizid
left
join questions qs
on qs.subcatid = qq.subjectid
where c.catid = q.catid
AND q.catid = 1
AND q.status = 'Active'
AND q.enddate >= '2016-05-02'
group
by q.quizid
This query give result all quiz table data and related user_quiz_results table.
If I write query using condition for left join table like userid=90, I don't get the any of data.
I want all data of quiz and if table have result for userid=90 get data from table user_quiz_history.avg(score).

Write condition on left join clause.
select q.* , qrh.q_complete , sum(qrh.score) as score1 , sum(qrh.total_questions) as tot_que from categories c , quiz q left join user_quiz_results_history qrh on qrh.quiz_id = q.quizid AND qrh.userid=84 where q.catid = 1 AND q.status = 'Active' AND q.enddate >= '2016-05-02' group by q.quizid

Related

Sub query return more than one field

I have a table structure like this
Table Structure
Here I want to select all the data from bus_routines table with bus details and avaliable_seat which is calculated from buses.number_of_seat - reserved_seats.number_of_reserved_seat - booking.number_of_seat even if the data is not present in booking table and reserved_seats table too where the bus_routines.sector_from=Ktm ,bus_routines.sector_to=Pkr and bus_routines.date=2015-12-15
Relation between them are :
buses and bus_routines --> one to many bus_routines and booking -->
one to many bus_routines and reserved_seats --> one to many
I have tried the following query
SELECT r.* , b.* ,
(
SELECT b.number_of_seat - sum(booking.number_of_seat)-sum(reserved_seats.number_of_reserved_seat)
FROM bus_routines AS r
INNER JOIN buses AS b
ON b.id = r.bus_id
INNER JOIN
(SELECT number_of_seat , bus_routine_id FROM booking GROUP BY booking.bus_routine_id) AS booking
ON booking.bus_routine_id = r.id
INNER JOIN (SELECT number_of_reserved_seat , routine_id FROM reserved_seats GROUP BY reserved_seats.routine_id) AS reserved_seats
ON r.id = reserved_seats.routine_id
WHERE
r.sector_from = "KTM" AND
r.sector_to = "PKR" AND
r.departure_date = "2015-12-15"
) AS avaliable_seat
FROM bus_routines AS r
INNER JOIN buses AS b
ON b.id = r.bus_id
WHERE
r.sector_from = "KTM" AND
r.sector_to = "PKR" AND
r.departure_date = "2015-12-15"
HAVING avaliable_seat > 0
I get the result what I want but the avaliable_seat is same for all the row
I have tried another query too but it give me the single result
SELECT r.* , b.* , b.number_of_seat - sum(booking.number_of_seat)-sum(reserved_seats.number_of_reserved_seat) AS available_seat
FROM bus_routines AS r
INNER JOIN buses AS b
ON b.id = r.bus_id
INNER JOIN
(SELECT number_of_seat , bus_routine_id FROM booking GROUP BY booking.bus_routine_id) AS booking
ON booking.bus_routine_id = r.id
INNER JOIN
(SELECT number_of_reserved_seat , routine_id FROM reserved_seats GROUP BY reserved_seats.routine_id) AS reserved_seats
ON r.id = reserved_seats.routine_id
WHERE
r.sector_from = "KTM" AND
r.sector_to = "PKR" AND
r.departure_date = "2015-12-15"
HAVING available_seat > 0
I also tried another query and it give me Subquery returns more than 1 row . The query is
SELECT r.* , b.* ,
b.number_of_seat - (SELECT sum(number_of_seat) FROM booking GROUP BY booking.bus_routine_id)
- (SELECT sum(number_of_reserved_seat) FROM reserved_seats GROUP BY reserved_seats.routine_id) AS available_seat
FROM bus_routines AS r
INNER JOIN buses AS b
ON b.id = r.bus_id
WHERE
r.sector_from = "KTM" AND
r.sector_to = "PKR" AND
r.departure_date = "2015-12-15"
HAVING available_seat > 0
One approach is to use correlated subqueries to get the reserved_seats and booked_seats for each bus_routine.
Let's assume that this query returns the rows you are wanting to return, it's just missing the available_seat column you want calculated:
SELECT r.*
, b.*
FROM bus_routines r
JOIN buses b
ON b.id = r.bus_id
WHERE r.sector_from = 'KTM'
AND r.sector_to = 'PKR'
AND r.departure_date = '2015-12-15'
To number of "reserved seats" for a given bus_routine, you can query the reserved_seats table, like this:
SELECT IFNULL(SUM(s.number_of_reserved_seat),0)
FROM reserved_seats s
WHERE s.routine_id = '649'
And the number of "booked seats" for a given bus_routine can be returned from the booking table, like this:
SELECT IFNULL(SUM(k.number_of_seat),0)
FROM booking k
WHERE k.bus_routine_id = '649'
We can incorporate the queries to get "reserved seats" and "booked seats" into the first query, as correlated subqueries. In place of the literal '649', with a reference the id from the bus_routine table.
SELECT r.*
, b.*
-- number of booked seats
, ( SELECT IFNULL(SUM(k.number_of_seat),0)
FROM booking k
WHERE k.bus_routine_id = r.id
) AS booked_seats
-- number of reserved seats
, ( SELECT IFNULL(SUM(s.number_of_reserved_seat),0)
FROM reserved_seats s
WHERE s.routine_id = r.id
) AS reserved_seats
-- calculate available seats as
-- (bus number_of_seat) - (booked_seats) - (reserved_seats)
, ( b.number_of_seat
- ( SELECT IFNULL(SUM(k.number_of_seat),0)
FROM booking k
WHERE k.bus_routine_id = r.id
)
- ( SELECT IFNULL(SUM(s.number_of_reserved_seat),0)
FROM reserved_seats s
WHERE s.routine_id = r.id
)
) AS avaliable_seat
FROM bus_routines r
JOIN buses b
ON b.id = r.bus_id
WHERE r.sector_from = 'KTM'
AND r.sector_to = 'PKR'
AND r.departure_date = '2015-12-15'
If there's no requirement to return the booked_seats and reserved_seats columns, those can be omitted from the query. The subqueries to get those values can just appear in the calculation of the available_seat column.
SQL Fiddle demonstration here: http://sqlfiddle.com/#!9/64eaa/7
Please try GROUP_CONCAT for return one column from multiple in subquery.
refer the links.
How to use GROUP_CONCAT in a CONCAT in MySQL
http://www.w3resource.com/mysql/aggregate-functions-and-grouping/aggregate-functions-and-grouping-group_concat.php

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/

Loading details from multiple tables

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

MySQL Query left join repeated records

With a Left Join i have this result.
Here the screen
http://f.cl.ly/items/373Y141r1K131d0n3f1q/Schermata%202013-04-01%20alle%2016.51.18.png
i want to show only record once time, without repeat it, but with a left join all my records are different.
what i have to do for show once all my records?
the query.
SELECT * FROM login_users
LEFT JOIN login_users_seguaci
ON login_users.user_id = login_users_seguaci.following
WHERE name LIKE ""
AND user_id != '1'
ORDER BY data DESC
SELECT x.*, y.*
FROM login_users x
LEFT JOIN
(
SELECT a.*
FROM login_users_seguaci a
INNER JOIN
(
SELECT following, MAX(DATA) max_data
FROM login_users_seguaci
GROUP BY following
) b ON a.following = b.following AND
a.DATA = b.max_date
) y ON x.user_id = y.following
// WHERE ... your condition here ...
ORDER BY t.data DESC

MySQL joining tables with ORDER BY gives unexpected result

I have this database design:
**users_tbl**
id
username
name
**posts_tbl**
id
url
users_id *FK REFERENCE to users table*
**posts_contents_tbl**
id
posts_id *FK REFERENCE to posts table
title
description
date
views
click
isDeleted
I'm using this query
SELECT a.name,a.username,c.*
FROM users_tbl a
LEFT JOIN posts_tbl b ON a.id = b.users_id
LEFT JOIN posts_contents_tbl c ON b.id = c.posts_id
ORDER BY c.id ASC
Why I try to run this query it gives me NULL results, sample output is like this
But when I try to remove the ORDER BY c.id ASC it gives me this output:
That's not my expected result.
My expected result would be it will display the posts_contents_tbl in Ascending order at the same time it won't show some null values. Some users in my database doesn't have posts data in the posts_tbl so they should not show too.
How would I do that one? Your help would be greatly appreciated and rewarded!
Thanks!
PS: I already have thousands record in my database.
In that case, you have to use INNER JOIN instead of LEFT JOIN because you only want users with posts to show. The reason why there are Null values is because the records are based on table users_tbl and you've mentioned that some of them have no post. Right?
Try this:
SELECT a.name,
a.username,
c.*
FROM users_tbl a
INNER JOIN posts_tbl b
ON a.id = b.users_id
INNER JOIN posts_contents_tbl c
ON b.id = c.posts_id
ORDER BY c.`date` DESC
I think this is what you are looking for:
SELECT a.name,a.username,c.*
FROM users_tbl a
INNER JOIN posts_tbl b
ON a.id = b.users_id
LEFT JOIN posts_contents_tbl c
ON b.id = c.posts_id
ORDER BY IFNULL(c.id, 0) ASC;
If you really needs that posts_tbl data should not display if not available.And all data of posts_contents_tbl then you need a RIGHT JOIN and INNER JOIN .
The Query like :-
SELECT a.name,a.username,c.*
FROM users_tbl a
INNER JOIN posts_tbl b ON a.id = b.users_id
RIGHT JOIN posts_contents_tbl c ON b.id = c.posts_id
ORDER BY c.id ASC;

Categories