SQL Joins across multiple tables - php

I am building an online survey system for which I wish to produce statistics. I want query based on the gender of the user. I have the following tables:
survey_question_options
survey_answer
users
I have constructed the following query so that it brings back a null response where there are no answers to the question:
SELECT COUNT(sa.option_id) AS answer , so.option_label
FROM survey_answer sa
RIGHT JOIN survey_question_options so
ON sa.option_id = so.option_id AND
sa.record_date>='2011-09-01' AND
sa.record_date<='2012-08-01'
LEFT JOIN users u
ON (sa.uid = u.uid AND u.gender='F')
WHERE so.question_id=24
GROUP BY so.option_label
ORDER BY so.option_id ASC
My query returns the following results set:
0 Red
1 Yellow
0 Blue
0 Green
However, the gender condition in the LEFT JOIN appears to be ignored in the query. When I change the gender to 'M' the same result is returned. However, the expected result would be 0 for everything.
I am not sure where I am going wrong. Please help.
Thanks in advance.

Well, you are doing a COUNT on a column from the main table, so the gender condition on the LEFT JOIN won't affect the result. You should do the COUNT on a column from the users table. I'm not sure if this is what you want, but you should try:
SELECT COUNT(u.uid) AS answer , so.option_label
FROM survey_answer sa
RIGHT JOIN survey_question_options so
ON sa.option_id = so.option_id AND
sa.record_date>='2011-09-01' AND
sa.record_date<='2012-08-01'
LEFT JOIN users u
ON (sa.uid = u.uid AND u.gender='M')
WHERE so.question_id=24
GROUP BY so.option_label
ORDER BY so.option_id ASC

The left join to the users table is evaluated after the join to the answer table - so although the user record is not returned if the user is the wrong gender, the answer record will be returned (regardless of the user's gender). Try:
SELECT COUNT(sa.option_id) AS answer , so.option_label
FROM (select a.option_id
from survey_answer a
JOIN users u ON a.uid = u.uid AND u.gender='F'
where a.record_date>='2011-09-01' AND
a.record_date<='2012-08-01') sa
RIGHT JOIN survey_question_options so
ON sa.option_id = so.option_id
WHERE so.question_id=24
GROUP BY so.option_label
ORDER BY so.option_id ASC

You're putting your condition in the wrong block. Since you're performing a LEFT JOIN, (which is a left-bound outer join) everything in the left table (the main table) is selected, together with the data from the joined table, where applicable. What you want is to add the data from all users and then restrict the full output of the query. What you've actually done is add the user data from only the female users and then displayed all data.
Sounds technical, but all you have to do is move the AND u.gender='F' into the main WHERE clause instead the ON clause. That will cause SQL to only select the rows for female users after the JOIN has taken place.

Related

Mysql request SELECT with LEFT JOIN

I am a beginner in MySQL, I had some courses in mysql that are now finished, I have a homework , provided by our teacher, to make for my training but I block to retrieve data from the base for a social network site. I understand the basis of JOIN but I still have trouble understanding the logic of LEFT JOIN OR OTHER type INNER JOIN ...
Here is my problem, I have a database with 2 tables,
member(id_member*, login, photo)
friend(id_member_request*, id_member_accept*, accept, date_acceptation)
The accept field of the friend table is a field that allows me to validate if they have friends by setting the value to 1 instead of 0.
The fields id_member_request and id_member_accept agree to the id_member of the member table.
I want to retrieve the login and the picture of the members who are friends, to be able to display them then.
I tested several queries:
SELECT m.login
, m.photo
FROM friend AS a
LEFT
JOIN member AS m
ON m.id_member = a.id_member_accept
LEFT
JOIN member AS m1
ON m1.id_member = a.id_member_request
WHERE accept = 1;
The query works but does not show all friendly members and even several times some people.
In RIGHT JOIN The result is NULL.
In INNER JOIN no result.
Thanks in advance because I blocked for several hours and I confess to lose myself a little. ;-)
Formatting the statement will help you :)
Okay, so you are joining the same column on both m and m1.
The following will bring back the data, but you have to pass in id_member_request..
DECLARE INT #MEMBERID = 2; --Example ID
SELECT m.login, m.photo
FROM friend AS a
LEFT JOIN member AS m ON m.id_member = a.id_member_accept
WHERE a.id_member_request = #MEMBERID
AND a.accept = 1;
This is bring back the m.login, m.photo for all of the friends for the Member with the ID #MEMBERID;
I think you want the friends of person who logged in. means for particular Member_Id here is query that will help you
select * from friend a
inner join member b on (a.id_member_request=b.id_member or a.id_member_accept=b.id_member) and b.id_member=1
where accept=1
where 1 is a member id whose friends will be displayed
You could re-write your query as in the below example
SELECT `m`.`id_member` AS `memberID`,
`m`.`login` AS `memberLoginName`,
`m`.`photo` AS `memberPhoto`,
`m1`.`id_member` AS `friendID`,
`m1`.`login` AS `friendLoginName`,
`m1`.`photo` AS `friendPhoto`,
`a`.`accept_date` AS `acceptDate`
FROM `friends` AS `a`
INNER JOIN `member` AS `m` ON `a`.`id_member_request` = `m`.`id_member`
INNER JOIN `member` AS `m1` ON `a`.`id_member_accept` = `m1`.`id_member`
WHERE `a`.`accept`=1
ORDER BY `a`.`id_member_request`,`a`.`id_member_accept` ASC
View this example in SQL fiddle
Thanks a lot for your help ;-)
I've tried all your answers and with a little change, this one works fine even in INNER, LEFT, RIGHT JOIN...
SELECT login,photo
FROM friend AS a
INNER JOIN member AS m ON ( a.id_member_request = m.id_member
OR a.id_member_accept = m.id_member )
WHERE m.id_member !=$id_member
AND a.accept=1;

MySql LEFT join multiple table same with same id or name

i'm having some problems. with the JEFT JOIN sql statement.
i have 3 tables:
user , products, prod_images.
table.user user_id
table.products user_id item_id
table.prod_images user_id item_id
when i run this query to get data that relation, its work fine.
but only work if the prod_images table are not empty.
when the prod_images empty
the sql right join merge the result and i get null to the products.item_id array
SELECT products.*, prod_images.*, users.*
FROM products
LEFT JOIN prod_images
ON products.item_id=prod_images.item_id
AND prod_images.is_primary = '1'
JOIN users
ON users.user_id=products.seller_id
WHERE products.status = '1'
ORDER BY created DESC
How can i make this query work fine when the table.prod_images
is empty ?
I believe your query is almost ok.
And it returns everything you asked for, but you should avoid of requesting products.*, prod_images.*, users.* because it is easy to confuse ourselves.
So if you change this part at least to SELECT products.item_id PRODUCTS_ITEM_ID, products.*, prod_images.*, users.*
And one another note, your query probably returns no products.item_id not when prod_images has no related records (because it LEFT JOINed) but when users has no related records because it is INNER JOINed. So I would start from changing JOIN users to LEFT JOIN users.
PS Look at #Reno comment. I did not mention that. for sure you should change ON products.item_id=products.item_id to ON products.item_id=prod_images.item_id

Limit LEFT JOIN results to 1 with flexible where clause

my query looks like that:
SELECT
count(users.id)
FROM users
LEFT JOIN mail_sender_jobs_actions ON mail_sender_jobs_actions.userID = users.id
LEFT JOIN table2 ON table2.userID = users.id
LEFT JOIN table3 ON table3.userID = users.id
WHERE {$flexibleWhereClause}
Now, the mail_sender_jobs_actions table CAN (doesnt need to return anything) return multiple entries. I dont want to group the results but still limit the returns of mail_sender_jobs_actions to 1 so I dont get duplicates... Otherwise the count wouldnt work properly.
Scraped the whole web and found nothing working for me as I want to keep the where clause flexible. Any solution?
EDIT
so to explain the situation. We have a table with users (users). We have a table with actions (mail_seder_jobs_actions). We have other tables related to that query which are not relevant (table1, table2, table3)
If a user does an action, an entry is being created in the actions table.
The where clause is flexible, meaning it is possible that somebody wants to only show users with a specific action.
It is also possible that an action is not relevant to the user, so this entry gets ignored.
With where criteria you have there is no point using left join, since the where criteria applies to the table on the right hand side, effectively turning the left join into an inner join.
Apparently yo do not use any columns from the right hand side table, so instead of using joins, I would use an exists subquery.
SELECT
1 as count,
users.email
FROM users
WHERE EXISTS (SELECT 1
FROM mail_sender_jobs_actions
WHERE mail_sender_jobs_actions.userID = users.id
AND mail_sender_jobs_actions.type = '1'
AND mail_sender_jobs_actions.jobID = '106'
AND {$flexibleWhereClause})
However, there is little point in having the count() because it will always return 1. If you want to count how many records each user has in the mail_sender_jobs_actions table, then you have to use left join, group by, and move the where criteria into the join condition:
SELECT
count(mail_sender_jobs_actions.userID),
users.email
FROM users
LEFT JOIN mail_sender_jobs_actions ON mail_sender_jobs_actions.userID = users.id
AND mail_sender_jobs_actions.type = '1'
AND mail_sender_jobs_actions.jobID = '106'
AND {$flexibleWhereClause}
GROUP BY users.email

sql query with left joins and preventing null results (datatables)

I am using datatables with a modified ssp.class.php to allow for joins and other custom features. In the example below I only want to return results from computers for the first x rows sorted by their id. Because of this, I list computers with my conditions first then LEFT JOIN users followed by logs (the information I am after).
It works great, BUT because of the left joins I have empty results. For instance, while my result set only contains logs from the correct computers... if logs has no rows for a particular user/computer combination I have a row with empty log data, but with user/computer data which serves me no purpose.
Is my only option to include a WHERE condition to prevent null values in the logs join... WHERE logs.user_id != '' or is there some other logic I can do in the select that I am missing?
SELECT (*see note)
FROM
( SELECT account_id, computer_id, computer_name
FROM computers
ORDER BY computer_id ASC LIMIT 0, ".$_SESSION['user']['licenses']."
) as c
LEFT JOIN users
on users.computer_id = c.computer_id
LEFT JOIN logs
on logs.user_id = users.user_id
You can use just JOIN for the table logs.
You put a LIMIT in the Derived Table accessing the computers table returning nrows. When an Inner Join or a final WHERE-condition filters some rows you will get less than nrows.
If this is not ok for you and you always want nrows, the only way is to move the LIMIT after doing Inner Joins:
SELECT (*see note)
FROM computers as c
JOIN users
on users.computer_id = c.computer_id
JOIN logs
on logs.user_id = users.user_id
ORDER BY computer_id ASC LIMIT 0, ".$_SESSION['user']['licenses']."
But this will probably be [much] slower...

PHP MySQL Left Join also using WHERE x IN y

I have been trying to get the following SQL to work:
SELECT user.id,
user.firstname,
user.surname,
user.gender,
user.username,
user.school_name,
user.grade,
user.vehicle,
user.price,
COUNT(student_reviews.userid) as reviews
FROM user
LEFT JOIN student_reviews ON student_reviews.userid = user.id
WHERE user.id IN ($results)
GROUP BY (student_reviews.userid)
The $results variable I'm passing in is '2,13,1'.
I only ever seem to get the first row back. It was working until I changed it to a left join to accommodate users that don't have any reviews.
I'm sure it's something simple, I'm just stumped.
Thanks
Update
It appears that when adding COUNT() or GROUP BY to the query, it only returns one result. It works until the COUNT() function is added
The problem here is that you are grouping by the column which can be null: student_reviews.userid (it's evaluated to null by left join for users without reviews). You should be grouping by user.id, and then you should get count zero for users without reviews.
I'm guessing that the user you are getting as a single result has at least 1 review, while others have none.
Why not try a subquery to get the number of reviews?
SELECT user.id,
user.firstname,
user.surname,
user.gender,
user.username,
user.school_name,
user.grade,
user.vehicle,
user.price,
(
SELECT COUNT(*) FROM student_reviews sr WHERE sr.userid = user.id
) as reviews
FROM user
WHERE user.id IN ($results)
When solving similar problems I go this route, because left join + group by usually doesn't give me the kind of results I want.

Categories