MySQL Join Table with String of other Records - mysql

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

Related

MySQL not null in left join condition

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;

mysql join with two row into one

I have 2 tables table name is users and projects.the structure of table is:
user table
id | name | role
1 | samjad | user
2 | saneer | constructor
projects table
id | name | user_id | constructor_id |
1 | school | 1 | 2 |
How can i get all details from both table in a single row based on project table id.
i want to select
projectname username, constroctorname, user_id, constroctor_id
in a single row
You can join the user table twice - Once as users and then as constructors.
select p.name as projectname,
u.name as username,
c.name as contructorname,
p.user_id,
p.contructor_id
from projects p
left join user u on p.user_id = u.id
left join user c on p.contructor_id = c.id
where u.role = 'user' -- check if the said user has role "user"
and c.role = 'constructor'; -- check if the said constructor has role "constructor"
Do the fact you have two relation between project table and user (one for user and one for constroctor) You can use user joined for two time
select p.name, u1.username, u2.username, p.user_id, p.constroctor_id
from projects as p
inner join user as u1 on p.user_id = u1.id
inner join user as u2 on p.constroctor_id = u2.id
You can use concat() function:
SELECT CONCAT(field1, field2, field3);
https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_concat
or CONCAT_WS(separator,str1,str2,...)
Assuming there is a table called Constructor with columns name and constructor_id your query would be
select
p.name as projectname,
u.name as username,
c.name as constructorname,
u.id as userid,
c.id as constructorid
from
projects p
inner join user u on p.user_id=u.id
inner join constructor c on p.constructor_id=c.id
Use a join. You probably want an INNER JOIN:
SELECT * -- actually include only the fields you need
FROM Projects p
INNER JOIN Users u ON u.id = p.user_id
INNER JOIN Users uc ON uc.id = p.constructor_id
You'll want to join the tables on their keys. In this case something like the below:
select p.name as projectname
, u.name as username
, if(u.role='constructor',u.name,null) as constructorname
, p.user_id, p.constructor_id
from users u
join projects p
on p.user_id = u.id;

Why my query shows no errors, but also returns zero rows?

I have written this query to get as many rows as there are users + count of potentials that each user have created + all potentials that have been converted. This is how it looks like:
SELECT u.*, p.allPotentials, pc.cPotentials
FROM os_user u
JOIN (SELECT FID_author, count(*) allPotentials FROM os_potential) p
ON p.FID_author = u.ID
JOIN (SELECT converted, FID_author, count(*) cPotentials FROM os_potential) pc
ON p.FID_author = u.ID AND pc.converted = 1
I am trying to do it with uncorrelated subquery as this answer explained me, that I can combine my queries into 1. But im getting 0 rows.
My tables looks like this:
Users:
+----+------+-------+
| ID | Name | Email |
+----+------+-------+
Potentials:
+----+------+-------+------------+-----------+
| ID | Name | Email | FID_author | converted |
+----+------+-------+------------+-----------+
FID_author is foreign key, the user id.
My query is returning 0 rows and shows no errors. What am I doing wrong?
EDIT
So far my query:
SELECT u.*, p.allPotentials, pc.cPotentials
FROM os_user u
LEFT JOIN (SELECT FID_author, count(*) allPotentials
FROM os_potential GROUP BY FID_author) p
ON p.FID_author = u.ID
LEFT JOIN (SELECT converted, FID_author, count(*) cPotentials
FROM os_potential GROUP BY FID_author) pc
ON p.FID_author = u.ID
AND pc.converted = 1
GROUP BY u.ID
I am getting results almost as expected, but the problem is, cPotentials contains 1 in every row, which is false. There are much many then only 1. Where could be the problem?
Missing group by on subquery and eventully use left join
SELECT u.*, p.allPotentials, pc.cPotentials
FROM os_user u
LEFT JOIN (SELECT FID_author, count(*) allPotentials FROM os_potential
GROUP BY FID_author) p
ON p.FID_author = u.ID
LEFT JOIN (SELECT converted, FID_author, count(*) cPotentials FROM os_potential
GROUP BY converted,FID_author) pc
ON pc.FID_author = u.ID AND pc.converted = 1

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.

Left Join 2 tables on 1 table

It must be pretty easy, but i can't think of any solution nor can I find an answer somewhere...
I got the table 'users'
and one table 'blogs' (user_id, blogpost)
and one table 'messages' (user_id, message)
I'd like to have the following result:
User | count(blogs) | count(messages)
Jim | 0 | 3
Tom | 2 | 3
Tim | 0 | 1
Foo | 2 | 0
So what I did is:
SELECT u.id, count(b.id), count(m.id) FROM `users` u
LEFT JOIN blogs b ON b.user_id = u.id
LEFT JOIN messages m ON m.user_id = u.id
GROUP BY u.id
It obviously doesn't work, because the second left join relates to blogs not users. Any suggestions?
First, if you only want the count value, you could do subselects:
select u.id, u.name,
(select count(b.id) from blogs where userid = u.id) as 'blogs',
(select count(m.id) from messages where userid = u.id) as 'messages'
from 'users'
Note that this is just a plain sql example, I have no mysql db here to test it right now.
On the other hand, you could do a join, but you should use an outer join to include users without blogs but with messages. That would imply that you get several users multiple times, so a group by would be helpful.
If you use an aggregate function in a select, SQL will collapse all your rows into a single row.
In order to get more than 1 row out you must use a group by clause.
Then SQL will generate totals per user.
Fastest option
SELECT
u.id
, (SELECT(COUNT(*) FROM blogs b WHERE b.user_id = u.id) as blogcount
, (SELECT(COUNT(*) FROM messages m WHERE m.user_id = u.id) as messagecount
FROM users u
Why you code does not work
SELECT u.id, count(b.id), count(m.id)
FROM users u
LEFT JOIN blogs b ON b.user_id = u.id <<-- 3 matches multiplies # of rows *3
LEFT JOIN messages m ON m.user_id = u.id <<-- 5 matches multiplies # of rows *5
GROUP BY u.id
The count will be off, because you are counting duplicate items.
Simple fix, but will be slower than option 1
If you only count distinct id's, you will get the correct counts:
SELECT u.id, count(DISTNICT b.id), count(DISTINCT m.id)
FROM users u
LEFT JOIN blogs b ON b.user_id = u.id
LEFT JOIN messages m ON m.user_id = u.id
GROUP BY u.id