Limit LEFT JOIN results to 1 with flexible where clause - php

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

Related

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

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...

mysql join select explained

I have two tables: 'posts' and 'users' every post has a 'ref_id' column to get the user id who posted it.
Now, I am getting posts this way:
$this->db->query("SELECT * FROM posts WHERE time > '$timeLimit' LIMIT 50");
I can't understand how to join every result to get the poster related data as well. What I am doing right now is basically a loop inside a loop, where foreach of the result, get their user info. But it is pretty obvious that this is very wrong,
Apparently I need to start using joins, but how does one do it? this should be a really simple example to work with, I suppose.
Any help? Thank you.
SELECT posts.*, users.*
FROM posts
INNER JOIN users
ON posts.posted_by = users.id;
Like this:
SELECT
posts.*,,
users.Username
FROM posts
INNER JOIN users ON posts.ref_id = users.user_id;
Explanation:
To JOIN to any tables with each others, there are two things; the JOIN type and the join condition. There are three main types of join:
INNER JOIN, only the rows that match the join condition will be returned from the two tables no more rows. But:
LEFT OUTER JOIN, when you join two tables you will have one on the left of the join keyword and the other one will be in the right:
FROM Table1 <------------- This is the left table.
LEFT OUTER JOIN table2 .... <------------- This is the right table.
In LEFT OUTER JOIN the unmatched rows from the left table will be included in the result set.
RIGHT OUTER JOIN the unmatched rows from the right table will be included in the result set.
CROSS JOIN this will perform a Cartesian product from the two tables.
In our query, the query will reutrn all the users from the users table only if the ref_id equal to the user_id column form the posts table.
For more information and explanations:
A Visual Explanation of SQL Joins.
Another Visual Representation of SQL Joins.
Join syntax in MySQL
SELECT user.name
FROM users
INNER JOIN posts
ON posts.ref_id == user.id
AND posts.time > 50
http://www.w3schools.com/sql/sql_join_inner.asp

SQL Joins across multiple tables

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.

Mysql - Join matches and non-matches

This is related to my other question:
Managing Foreign Keys
I am trying to join the table of matches and non-matches.
So I have a list of interests, a list of users, and a list of user interests.
I want the query to return all interests, whether the user has the interest or not (should be null in that case), only where the user = x. Every time I get the query working its only matching interests that the user specifically has, instead of all interests whether they have it or not.
You should rather use LEFT JOINS
Something like
SELECT *
FROM interests i LEFT JOIN
userinterests ui ON i.interestID = ui.interestID LEFT JOIN
users u ON ui.userID = u.uiserID
WHERE userID = ?
where is the user id you are looking for.
SELECT *
FROM interests i
LEFT JOIN userinterests ui ON i.interestID = ui.interestID
LEFT JOIN users u ON ui.userID = u.uiserID
and u.userID = ?
If you put a where condition on a table that should have no records inteh main tbale, you convert the join from a left join to an inner join. The only time you should ever have a condition inthe where clasue for something one the right side of a left join is when you are searching for records that don't match (where u.userid is null, for instance)
Of course you should define the fields to be selected and never use select * in production code especially not when you have a join as it sends repeated information across the network (the data inteh join feilds is repeated) and is a waste of resources and poor prgramming practice for multiple reasons.

Categories