I have a query which selects values from different tables via JOIN. But I think I have a problem now, because one table needs to join in again with a different name, but somehow it doesn't work.
An example based on a social network strucutre:
Table "users":
+--------+-----------+
| userid | username |
+--------------------|
| 1 | userOne |
| 2 | userTwo |
| 3 | userThree |
+--------+-----------+
Table "posts":
+--------+---------+-------------------------------+
| postid | userid | text |
+--------------------------------------------------|
| 102 | 1 | "Haha i'm User one" |
| 103 | 1 | "And User one is the best" |
| 104 | 3 | "I'm having fun with user two"|
+--------+---------+-------------------------------+
Table "usertags":
+--------+---------------+
| postid | tagged_userid |
+------------------------|
| 104 | 2 |
+--------+---------------+
This is my query:
SELECT posts.postid,
posts.userid,
posts.text,
users.username,
IFNULL(GROUP_CONCAT(DISTINCT usertags.tagged_userid SEPARATOR ','), NULL) as
taggedusers_id,
IFNULL(GROUP_CONCAT(DISTINCT taggedusers.fullname SEPARATOR ','), NULL) as
taggedusers_name,
FROM posts
JOIN users ON posts.userid = users.userid
LEFT JOIN usertags ON posts.postid = usertags.postid
LEFT JOIN users as taggedusers ON usertags.tagged_userid = users.userid
GROUP BY posts.postid
ORDER BY posts.postid DESC
And that's the result i get:
+--------+---------+---------------------------------------------------------------------+
| postid | userid | text | username | taggedusers_id |
+-----------------------------------------------------------------------|----------------|
| 102 | 1 | "Haha i'm User one" | userOne | NULL |
| 103 | 1 | "And User one is the best" | userOne | NULL |
| 104 | 3 | "I'm having fun with user two"| userThree | 2
+--------+---------+-------------------------------+--------------------+----------------+
The problem:
The column 'taggedusers_name' shows up, but it always shows NULL. I want it to show the usernames of the users which are tagged.
Like this, but in the whole, big output
+---------------+-------------------+
| taggeduser_id | taggeduser_name |
+-----------------------------------|
| 2 | userTwo |
| 2,3 | userTwo,userThree |
| NULL | NULL |
+---------------+-------------------+
So, how is this possible? Do I need to make a multiple SELECT statement? I tried that already, but I failed at this too :/
I'd be glad for help!
The issue is that you're referencing users again without the alias. Since users is already being implicitly INNER JOIN (see this question What is the default MySQL JOIN behaviour, INNER or OUTER?) so the taggedusers table will have to meet the conditions that the author is the same as the tagged user ID.
SELECT posts.postid,
posts.userid,
posts.text,
users.username,
IFNULL(GROUP_CONCAT(DISTINCT usertags.tagged_userid SEPARATOR ','), NULL) as
taggedusers_id,
IFNULL(GROUP_CONCAT(DISTINCT taggedusers.fullname SEPARATOR ','), NULL) as
taggedusers_name,
FROM
posts
JOIN
users
ON posts.userid = users.userid
LEFT JOIN
usertags
ON posts.postid = usertags.postid
LEFT JOIN
users as taggedusers
ON usertags.tagged_userid = taggedusers.userid -- this is (I assume) what you meant
-- ON usertags.tagged_userid = users.userid -- this is your problem
GROUP BY
posts.postid
ORDER BY
posts.postid DESC
One way to avoid this is to always alias tables; in this case you could have aliased users as 'author' or something of the like.
The reason you didn't have the same problem with the distinct IDs is that it was on a table that was joined correctly.
Related
i have a following tables in MySQL database:
+------------------------+
| Users |
+----+--------+----------+
| id | name | role |
+----+--------+----------+
| 1 | Martin | admin |
+----+--------+----------+
| 2 | George | admin |
+----+--------+----------+
| 3 | John | employee |
+----+--------+----------+
+-------------------------+
| Forms |
+----+--------------------+
| id | type |
+----+--------------------+
| 10 | marketing_form |
+----+--------------------+
| 11 | client_survey_form |
+----+--------------------+
| 12 | client_survey_form |
+----+--------------------+
+---------------------------------------------+
| UsersAssignToForms |
+----+---------+---------+--------------------+
| id | user_id | form_id | additional_comment |
+----+---------+---------+--------------------+
| 20 | 1 | 10 | Lorem ipsum... |
+----+---------+---------+--------------------+
| 21 | 2 | 10 | Lorem ipsum.... |
+----+---------+---------+--------------------+
| 22 | 3 | 10 | null |
+----+---------+---------+--------------------+
| 23 | 3 | 11 | null |
+----+---------+---------+--------------------+
I would like to have result:
+---------+---------+------------+--------------------+--------------------+
| user_id | form_id | first_name | form_type | additional_comment |
+---------+---------+------------+--------------------+--------------------+
| 1 | 10 | Martin | marketing_form | Lorem ipsum... |
+---------+---------+------------+--------------------+--------------------+
| 3 | 11 | John | client_survey_form | null |
+---------+---------+------------+--------------------+--------------------+
| null | 12 | null | client_survey_form | null |
+---------+---------+------------+--------------------+--------------------+
First of all i would like to limit number of users returned from join query (one user per one form). If user with admin role is assigned to form i would like to display this user (prioritize admin role over employee role) and limit number of returned users to 1, if admin is not assign, but employee is assigned query should return this user, if no-one is assign query should return nulls (left or right join probably).
I saw this question on stackoverflow - MySQL JOIN with LIMIT 1 on joined table, but unfortunately first answer has n+1 issue and rest of answers was made with simple one join. For my purposes i need to join more tables but wouldn't like to design this tables above to clarify what i would like to achieve, but it's very important.
So my query will looks like probably:
SELECT u.id, f.id, u.name, f.type, uf.additional_comment, [more selects from other tables...] FROM Forms as f
LEFT JOIN Users as u ON ......
INNER JOIN UsersAssignToForms as uf ON .....
[here i would like to put more and more inner joins.....]
In MySql >= 8.0 you can number the rows using some criteria (for each Form starting from one and order by u.role ASC and u.id ASC), then you can filter rows with number one:
WITH sq AS (SELECT u.id AS user_id, f.id AS form_id, u.name, f.type, uf.additional_comment,
ROW_NUMBER() OVER (PARTITION BY f.id ORDER BY u.role ASC, u.id ASC) AS num
FROM Forms AS f
LEFT JOIN UsersAssignToForms AS uf ON f.id = uf.form_id
LEFT JOIN Users AS u ON u.id = uf.user_id)
SELECT *
FROM sq
WHERE num = 1;
Before MySql 8.0 you can try something like this (the idea is the same but with different implementation):
SELECT sq2.user_id, sq2.form_id, sq2.name, sq2.type, sq2.additional_comment
FROM (
SELECT
sq1.*,
#row_number:=CASE WHEN #form_id = sq1.form_id THEN #row_number + 1 ELSE 1 END AS num,
#form_id:= sq1.form_id
FROM (SELECT u.id AS user_id, f.id AS form_id, u.name, f.type, uf.additional_comment
FROM Forms AS f
LEFT JOIN UsersAssignToForms AS uf ON f.id = uf.form_id
LEFT JOIN Users AS u ON u.id = uf.user_id
ORDER BY f.id ASC, u.role ASC, u.id ASC) AS sq1
ORDER BY sq1.form_id) AS sq2
WHERE sq2.num = 1;
I have a many to many user-sport relation and I'm getting a concatenated string of all sport names a user plays using a subquery. My structure looks like:
user
===============
| id | name |
---------------
| 1 | Hugo |
| 2 | Paco |
| 3 | Luis |
---------------
sport
=================
| id | name |
-----------------
| 1 | tennis |
| 2 | football |
| 3 | handball |
-----------------
user_sport
======================
| user_id | sport_id |
----------------------
| 1 | 3 |
| 1 | 1 |
| 2 | 1 |
----------------------
how do I filter the results with users that play any sport from a list, for example getting all users who play tennis or handball.
I'm trying with this query:
SELECT u.id, u.name,
COALESCE (
(SELECT GROUP_CONCAT(s.name SEPARATOR ', ')
FROM sport AS s
LEFT JOIN user_sport AS us ON us.sport_id = s.id
WHERE us.user_id = u.id),'') AS sports
FROM user u
WHERE us.sport_id IN (1,3)
GROUP BY u.id
ORDER BY g.name
but it is not working because the where clause doesn't know the user_sport table. So, I have to create a new JOIN outside the subquery?
You have to join with user_sport twice. Once to filter it to just the sports in the list, and the other time to get all the sports that the selected users play in.
SELECT u.id, u.name, GROUP_CONCAT(s.name SEPARATOR ', ') AS sports
FROM user AS u
JOIN user_sport AS us_filtered ON u.id = us_filtered.user_id
JOIN user_sport AS us_all ON u.id = us_all.user_id # all
JOIN sport AS s ON s.id = us_all.sport_id
WHERE us_filtered.sport_id IN (1, 3)
GROUP BY u.id
ORDER BY u.name
I need to constrain a user query regarding user roles (user or admin).
In a datatbase there is a table user_scope containing all user ids and the assigned roles (every user has one entry with a 1 for user, and some have a second entry with 2 for admin). I can't change that database architecture right now.
This is my SELECT on a table users so far which joins data from other tables
SELECT
u.id,
GROUP_CONCAT(DISTINCT scopes.scope ORDER BY scopes.id ASC SEPARATOR ' ') as scope
FROM users as u
LEFT JOIN user_scope on user_scope.user_id = u.id
LEFT JOIN scopes on scopes.id = user_scope.scope_id
The table users
+----+--------+
| id | parent |
+----+--------+
| 1 | Alex |
| 2 | Marc |
| 3 | Cath |
+----+--------+
The table user_scope
+---------+----------+
| user_id | scope_id |
+---------+----------+
| 1 | 1 |
| 1 | 2 |
| 2 | 1 |
| 3 | 1 |
+---------+----------+
The table scopes
+----+--------+
| id | scope |
+----+--------+
| 1 | user |
| 2 | admin |
+----+--------+
This will yield something like this
+----+------------+
| id | scope |
+----+------------+
| 1 | user admin |
| 2 | user |
| 3 | user |
+----+------------+
The problem arises when I want to filter the results based on one particular role. I tried this
SELECT
u.id,
GROUP_CONCAT(DISTINCT scopes.scope ORDER BY scopes.id ASC SEPARATOR ' ') as scope
FROM users as u
LEFT JOIN user_scope on user_scope.user_id = u.id
LEFT JOIN scopes on scopes.id = user_scope.scope_id and scopes.id = 2
Or 1 respectively. However, that will not reduce the number of returned rows but will only NULL the rows with users that have not the scope admin. I also tried to use a CASE but I can't neither use this in a WHERE statement.
How do I reduce the rows returned in this context? Help is REALLY appreciated.
Use a having clause:
SELECT u.id,
GROUP_CONCAT(DISTINCT s.scope ORDER BY s.id ASC SEPARATOR ' ') as scopes
FROM users u LEFT JOIN
user_scope us
ON us.user_id = u.id LEFT JOIN
scopes s
ON s.id = us.scope_id
GROUP BY u.id
HAVING MAX(s.id = 2) > 0;
I want to create a query for project listings that would give the number of registered applications, excluding the ones for which the user does not exist.
In this case, considering user 10 does not exist, I should have the query results as folows:
RESULTS
+----+------------+--------------+
| id | project | applications |
+----+------------+--------------+
| 1 | MyProject1 | 3 |
| 2 | MyProject2 | 0 |
| 3 | MyProject3 | 0 |
+----+------------+--------------+
TABLES
Projects
+----+------------+
| id | name |
+----+------------+
| 1 | MyProject1 |
| 2 | MyProject2 |
| 3 | MyProject3 |
+----+------------+
applications
+----+------+------------+
| id | user | project_id |
+----+------+------------+
| 1 | 3 | 1 |
| 2 | 4 | 1 |
| 3 | 5 | 1 |
| 4 | 10 | 1 |
+----+------+------------+
users
+----+---------+
| id | Name |
+----+---------+
| 1 | Smith |
| 2 | John |
| 3 | Paul |
| 4 | Chris |
| 5 | Gabriel |
+----+---------+
The below query is not excluding the non-existing users:
SELECT `projects` . * , (
SELECT COUNT( * )
FROM `applications`
WHERE `applications`.`project_id` = `projects`.`id`
AND EXISTS (
SELECT `applications`.`id`
FROM `applications` , `users`,`project`
WHERE `application`.`user` = `users`.`id` AND `applications`.`project_id` = `project`.`id`
)
) AS `applications`
FROM `projects` ORDER BY `id` DESC LIMIT 30
I think you want left join and group by:
select p.id, p.name, count(u.id)
from projects p left join
applications a
on p.id = a.project_id left join
users u
on a.user_id = u.id
group by p.id, p.name;
However, you might want to think about fixing the data. It seems like there should be foreign key relationships between applications and projects and applications and users. The ability to have an invalid user means that there is no valid foreign key relationship to users.
Your query looks overly complicated. This should do:
select
id,
name as project,
(
select count(*)
from applications a
where a.project_id = p.id
and a.user in (select id from users)
) as applications
from projects p;
Based on previous solution
select p.id, p.name, count(u.id)
from projects p left join
applications a
on p.id = a.project_id left join
users u
on a.user = u.id
where u.id is not null
group by p.id, p.name;
When you do a left join, if the search value doesn't exists, it returns null. Then filtering by excluding null users, will give you the result.
Please find a sqlfiddle to illustrate it : http://www.sqlfiddle.com/#!9/cbfec6/3
But easiest solution would be
select p.id, p.name, count(u.id)
from projects p,applications a, users u
where a.user = u.id
and p.id = a.project_id
group by p.id, p.name;
I'm developing an application integrated with facebook. This application can be embedded in FB page as tab app.
Using FB SDK feeds of page will be stored in Feeds table.
Page fans will may have liked and commented on feeds posted by page.
Users' likes store in Like Table and users' comments store in Comment table
I want to get total count ( Likes count + comment count) of each users'.
SQL Fiddle : http://sqlfiddle.com/#!2/ecb37/10/0
Table : Feeds
| ID | POST_ID |
|----|---------------------------------|
| 56 | 150348635024244_795407097185058 |
| 55 | 150348635024244_795410940518007 |
| 54 | 150348635024244_795414953850939 |
| 53 | 150348635024244_797424133650021 |
| 52 | 150348635024244_797455793646855 |
| 51 | 150348635024244_798997120159389 |
| 50 | 150348635024244_798997946825973 |
Table : Likes
SELECT user_id, COUNT(*) FROM likes GROUP by user_id
| USER_ID | LIKECOUNT |
|------------------|-----------|
| 913403225356462 | 4 |
| 150348635024244 | 3 |
| 356139014550882 | 2 |
| 753274941400012 | 2 |
| 1559751687580867 | 1 |
Table : Comments
SELECT user_id, COUNT(*) FROM comments GROUP by user_id
| USER_ID | COMMENTSCOUNT |
|-----------------|---------------|
| 150348635024244 | 2 |
| 356139014550882 | 2 |
| 913403225356462 | 2 |
Result should be like this
| POINTS | LIKESCOUNT | COMMENTSCOUNT | USER_ID |
|--------|------------|---------------|-----------------|
| 6 | 4 | 2 | 913403225356462 |
| 5 | 3 | 2 | 150348635024244 |
| 4 | 2 | 2 | 356139014550882 |
| 2 | 2 | 0 | 753274941400012 |
| 1 | 1 | 0 |1559751687580867 |
I tried this query. but count of each user's is wrong
SELECT COUNT(likes.user_id)+COUNT(comments.user_id) as points, likes.user_id FROM `likes`
LEFT JOIN comments ON likes.user_id = comments.user_id
LEFT JOIN feeds ON likes.post_id = feeds.post_id
WHERE likes.post_id LIKE '153548635024244%'
GROUP BY likes.user_id
ORDER BY points DESC
The two queries are unrelated and a join is useless. Use a UNION ALL:
SELECT user_id, sum(n) from (
SELECT user_id, COUNT(*) n FROM likes GROUP by user_id
UNION ALL
SELECT user_id, COUNT(*) FROM comments GROUP by user_id
) x
GROUP BY user_id
UNION ALL is needed instead of just UNION, because UNION removes duplicates and would cause incorrect results for the edge case of the two subqueries yielding the same counts.
The simple way to get what you want is to use count(distinct). But that will likely have lousy performance. Instead, use correlated subqueries:
SELECT COUNT(*) +
(select COUNT(c.user_id) from comments c where c.user_id = l.user_id)
) as points, l.user_id
FROM likes l
WHERE l.post_id LIKE '153548635024244%'
GROUP BY l.user_id
ORDER BY points DESC;
I'm not sure what the feeds table is for. However, you version of the query creates a cartesian product between the different tables. If you have a lot of activity for a given user, that would be very bad for performance.