MySQL not null in left join condition - mysql

My query looks like:
SELECT *
FROM users U
LEFT JOIN posts P ON P.userId = U.id AND P.userId IS NOT NULL;
Why the query also return result where userId is null ?
I know that for my needs I can use INNER JOIN to get only posts related to user but is so strange that LEFT JOIN support multiple conditions, but not work with NOT NULL conditions.

This is because "posts" does not contain the null-values and hence they can´t be filtered at that stage. The Null-values are only generated trough the join, when the server can´t find a corresponding row on the right table. So just put the not null in the where clause and it will work:
SELECT * FROM users U LEFT JOIN posts P ON P.userId = U.id WHERE userId IS NOT NULL;
(EDIT: You should use an inner join for productive work though, as it is the proper way and will give you much greater performance.)
You can also see all users who don´t have posts by inverting that:
SELECT * FROM users U LEFT JOIN posts P ON P.userId = U.id WHERE userId IS NULL;

You are outer joining the posts table. This means for every users record that has no match in posts you still get this record with all posts columns null.
So say you have a users record with userid = 5 and there is no posts record with id = 5.
ON P.userId = U.id AND P.userId IS NOT NULL
The two combined conditions are not met (there is no record with userid 5), so you get the users record with all posts columns set to null in your results.
Maybe you are simply looking for an inner join? All users records with their posts data?

This query:
SELECT *
FROM users U LEFT JOIN
posts P
ON P.userId = U.id AND P.userId IS NOT NULL;
Returns all rows in the users as well as all columns from posts, regardless of whether or not they match. This is true, regardless of whether the ON clause evaluates to TRUE or FALSE.
What you want is a WHERE. In addition, you should only select the columns from users:
SELECT u.*
FROM users U LEFT JOIN
posts P
ON P.userId = U.id
WHERE P.userId IS NOT NULL;
Note that you can also accomplish this using NOT IN or NOT EXISTS.

Because the LEFT JOIN must return every row from the left table by it's definition. The raw may be augmented with the data of the right table depending on the ON clause evaluation. So the following code must return a row.
select u.*, p.*
from (
select 1 as id
) u
left join (
-- no data at all
select 2 as id where 1=2
) p on 3 = 4 -- never is true

Try this
SELECT * FROM users U LEFT JOIN posts P ON P.userId = U.id
SELECT * FROM users U LEFT JOIN posts P ON P.userId = U.id where P.userId IS NOT NULL;

IS NOT NULL WITH JOINS
SELECT * FROM users
LEFT JOIN posts ON post.user_id = users.id
WHERE user_id IS NOT NULL;

Related

MYSql query with inner joins returns duplicate results

I am trying to get the posts by the users that the userID (? in the query) follows, AND get the posts by ? user, themselves.
SELECT
posts.id AS postid, posts.user AS user, posts.images AS images, posts.post_created, posts.textvalue, posts.textpost,
users.username AS authorname
FROM posts
INNER JOIN follows ON (posts.user = follows.follows AND follows.user = ?) OR posts.user = ?
INNER JOIN users ON users.id = posts.user
ORDER BY posts.post_created DESC LIMIT 20
This query works, but for every post that is by ? it returns the row twice, when I remove OR posts.user = ? it works fine, but does not show the posts for the ? user id.
I believe you want:
SELECT p.*, u.username AS authorname
FROM posts p INNER JOIN
users u
ON u.id = p.user LEFT JOIN
follows f
ON p.user = f.follows AND f.user = ?
WHERE f.user IS NOT NULL OR -- a match on the "follows" condition
p.user = ? -- a match on the post condition
ORDER BY p.post_created DESC
LIMIT 20;
Your query is a but confusing. But basically you have this condition for posts from the user:
FROM posts p INNER JOIN
follows f
ON p.user = ?
(forget the OR part -- that only further increases the number of rows.)
There is no JOIN condition between the two tables, so you are getting a row for every user that follows the post.

Making a query with several JOINS

I am stuck with some SQL query.
I have four tables. Which are connected:
user =>user_account=>acount_profile_entries=>profile_entries
From left to right they are one to many.
user_account has a user_id field as FK.
account_profile_field has user_account_id and profile_entry_id.
Profile_entries has a text field that I need to show for each user (account).
I need to write a query that will show me, all accounts for every user, and its profile entries.
I am sorry if this is confusing, I tried to make it simple
This is what I have done so far. I can show all accounts for every user and this is the point I am stuck with. Last two commented out Joins are not working properly. I believe I am close somewhat, I just need a push :)
SELECT
u.email AS Email,
u.id AS UserId,
ua.id AS UserAccountId,
ua.app_id AS Application
FROM users AS u
INNER JOIN user_accounts ua ON ua.user_id = u.id
-- INNER JOIN account_profile_entries ape ON ape.user_account_id = ua.id
-- INNER JOIN profile_entries as pe ON pe.id = ape.profile_entry_id
limit 10
Try this SQL Query with using LEFT JOIN
Description :- The MySQL LEFT JOIN joins two tables and fetches rows based on a condition, which is matching in both the tables and the unmatched rows will also be available from the table written before the JOIN clause.
SYNTAX
SELECT column_name(s)
FROM table1
LEFT JOIN table2 ON table1.column_name = table2.column_name;
SELECT u.*,
u.id AS UserId,
ua.id AS UserAccountId,
ua.app_id AS Application,pe.* FROM `users` u
LEFT JOIN user_accounts ua ON ua.user_id = u.id
LEFT JOIN account_profile_entries ape ON ape.user_account_id = ua.id
LEFT JOIN profile_entries as pe ON pe.id = ape.profile_entry_id LIMIT 10

mysql join 2 tables with on condition , keep some values

I have 2 tables.
users(id,username) and links(id,usernameORid).
Example of rows: users{ [1,test] , [2,stack] } and links{ [1,overflow] , [2, 1] }
So, table links may contain username or id from table users. As you can see in the example,
usernameORid from links may not contain the id or username from users.
I hope you understood my example.
Now, i have this query:
SELECT l.usernameORid, u.username, u.id
FROM links l
LEFT JOIN users u
ON l.usernameORid= u.id
LEFT JOIN user_roles ur
ON ur.userID = u.id
WHERE ur.roleID < 4
group by u.id
But this query does not return rows from links if usernameORid is not an actual username or id from users.
In the previous example, will not return row [1,overflow]. I want that row too.
How can i achieve that?
EDIT: The problem is partialy related to
LEFT JOIN user_roles ur
ON ur.userID = u.id
WHERE ur.roleID < 4
but still, how can i achieve that?
user_roles ( id,userID,roleID)
Change your final WHERE condition to:
WHERE ur.roleID < 4 OR u.id IS NULL
This will allow it to return rows that didn't have a match in users. Normally a LEFT JOIN does that by itself, but since you're doing an additional join on that table, the WHERE clause is filtering those non-matching rows out because they don't have a roleID.
You can use an OR statement in your join between links and users. This will allow you to pick up users records where the link.usernameORid is equal to either the users.id or the users.username
SELECT l.usernameORid,
u.username,
u.id
FROM links l
LEFT JOIN users u ON
l.usernameORid = u.id OR
lusernameORid = u.username
LEFT JOIN user_roles ur
ON ur.userID = u.id
WHERE ur.roleID < 4
GROUP BY u.id
This will still cause records to drop if the found users->user_roles.roleID is less than 4. If you wanted to have link records maintained regardless of whether of a user was found by username or ID then you would need to subquery the users and user_roles table joins and apply your WHERE statement there instead. This query is below:
SELECT
l.usernameORid,
u.username,
u.id
FROM links l
LEFT JOIN
(
SELECT
users.username,
users.idusers
FROM
users
LEFT JOIN user_roles ON
user_roles.userID = users.id
WHERE
user_roles.roleID < 4
) u ON
l.usernameORid= u.id OR
l.usernameORid = u.username
group by u.id
Furthermore, if you wish the 2nd or 3rd column of your return to hold the value that is in l.usernameORid when the users table lacks a match... if your users.id is always numeric you could do some trickery with a CASE statement:
SELECT
l.usernameORid,
Coalesce(u.username, CASE WHEN .lusernameORid REGEXP '^[0-9]+$' THEN NULL ELSE l.usernameORid END) as username,
Coalesce(u.username, CASE WHEN .lusernameORid REGEXP '^[0-9]+$' THEN l.usernameORid ELSE NULL END) as userid
FROM links l
LEFT JOIN
(
SELECT
users.username,
users.idusers
FROM
users
LEFT JOIN user_roles ON
user_roles.userID = users.id
WHERE
user_roles.roleID < 4
) u ON
l.usernameORid= u.id OR
l.usernameORid = u.username
group by u.id
Keep in mind though, that if the users table doesn't have a match for the links.usernameORid then only the username OR the id could be determined, so you will have a NULL in one of the two fields.

MySQL Join Table with String of other Records

Can you help me cause all the records in the "preference" table to become a string inside the p.data returned result?
I could only think of CONCAT, but my inner join only selects 1 record.
Example Query
SELECT
u.userid,
p.data
FROM user u
INNER JOIN preference p ON (
p.userid = u.userid
)
WHERE u.userid = 1
Desired Output
userid | data
--------------
1 | 1,2,3,4,5
2 | 1,2,3,4
3 | 1,2,3
MySQL's GROUP_CONCAT() performs exactly this function:
SELECT
u.userid,
GROUP_CONCAT(p.data) AS data
FROM
user u
INNER JOIN preference p ON u.userid = p.userid
WHERE u.userid = 1
GROUP BY u.userid
You only get one row back because of your WHERE clause. Remove the WHERE clause to return rows for all users. And switch it to a LEFT JOIN if you want to return users having no related rows in the preference table.
Doesn't matter your Inner join only join one field.
You have to add group by
SELECT
u.userid,
GroupConcat(p.data) as Data
FROM user u
INNER JOIN preference p ON (
p.userid = u.userid
)
--WHERE u.userid = 1 With this you not gonna get the desired output.
group by u.userId

SQl queries left join not giving accurate results

I have been trying to join two tables (USERS AND USERS_ROLES) based on their role id I put the left join on following query
users.id = users_roles.fk_user_id
but the output is not correct of users_roles.fk_role_id coulmun and shows NULL where it should display the id of users.id = users_roles.fk_user_id that is 4 (at most places) because on users.id = users_roles.fk_user_id the value of users_roles.fk_role_id = 4
Kindly let me know how can i fix that so my query should result the exact vlaues of ids where they match,
Thanks
SELECT users.id, users.v_first_name, users.v_last_name, user_facility.fk_facility_id,users.fk_tenant_id, marital_status.v_marital_status,
users.v_blood_type, NOW(),users_roles.fk_role_id
FROM users
LEFT JOIN (user_facility, marital_status, users_roles) ON
users.id = user_facility.fk_user_id AND users.fk_marital_status_id=marital_status.id AND users.id = users_roles.fk_user_id
Usage of AND operator when used with Left or Right join gives different result. You should be clear what you are trying to accomplish..See this
well it is what you get by first implicitly inner-joining 3 tables and then explicitly left-joining the result to a 4th table only if 3 conditions relevant to all of the 3 inner-joinded tables are matched (i.e. when 3rd condition is false, nothing is joined from either of the 2 remaining tables)
i strongly suggest not to combine implicit and explicit joins, i personally use explicit joins all the time:
if you need an outer join:
SELECT ...
FROM users
LEFT JOIN user_facility ON users.id = user_facility.fk_user_id
LEFT JOIN marital_status ON users.fk_marital_status_id=marital_status.id
LEFT JOIN users_roles ON users.id = users_roles.fk_user_id
if you need an inner join:
SELECT ...
FROM users
JOIN user_facility ON users.id = user_facility.fk_user_id
JOIN marital_status ON users.fk_marital_status_id=marital_status.id
JOIN users_roles ON users.id = users_roles.fk_user_id
or if you prefere implicit inner joins for some obscure reason:
SELECT ...
FROM users,
user_facility,
marital_status,
users_roles
WHERE users.id = user_facility.fk_user_id
AND users.fk_marital_status_id=marital_status.id
AND users.id = users_roles.fk_user_id
(implicit outer joins are getting deprecated in all RDBMS as far as i know)
When it shows NULL it means there isn't a correspondency (relation) between all tables in the JOIN clause.
If you want to show only the ones that have relations in all tables, use INNER JOIN instead.
SELECT u.id,
u.v_first_name,
u.v_last_name,
uf.fk_facility_id,
u.fk_tenant_id,
ms.v_marital_status,
u.v_blood_type,
NOW(),
ur.fk_role_id
FROM users u
INNER JOIN user_facility uf ON u.id = uf.fk_user_id
INNER JOIN marital_status ms ON u.fk_marital_status_id=ms.id
INNER JOIN users_roles ur ON u.id = ur.fk_user_id