Selecting multiple values from same column with a single Select query - mysql

So, I've got two tables: users and tasks;
users:
user_id username password first_name last_name isAdmin
tasks:
task_id name description assessment assigned_user_id fk creator_id fk created_on last_modified status
What I want to do is replace assigned_user_id and creator_id with first_name + last_name from users table. So I execute the following query:
SELECT `task_id`, `description`,`assessment`,
(SELECT CONCAT(`first_name`, ' ',`last_name`) WHERE `Tasks`.`assigned_user_id` = `Users`.`user_id`) AS assigned_user,
(SELECT CONCAT(`first_name`, ' ',`last_name`) WHERE `Tasks`.`creator_id`=`Users`.`user_id`) AS creator_user,
`created_on`,`last_modified`,`status`
FROM `Tasks`
LEFT JOIN `Users`
ON Tasks.assigned_user_id = Users.user_id
OR Tasks.creator_id = Users.user_id
WHERE task_id=2
The problem is that it returns 2 rows. One is with assigned_user_id null and filled creator_id and the other is the other way around:
task_id description assessment assigned_user creator_user created_on last_modified status
2 SHA SA PII LI 24 NULL Petyo Chuliuv 2016-07-22 2016-07-22 1
2 SHA SA PII LI 24 Gosho Toshov NULL 2016-07-22 2016-07-22 1
Question is: How to return a single row with assigned_user and creator_user filled and where I did wrong? Thanks in advance!

I tested this on SQL Server and reproduced the same issue as you so hopefully I can be of help.
When I did the test the two SELECT CONCAT statements were using the same user_id both times. So the issue seems that it is not checking for both ids at once but both ids at separate times. So if I were to use your example it first uses Petyo's id in both of the SELECT CONCAT statements (only filling the creator_user role so the other one becomes false) and then it uses Gosho's id in both of the SELECT CONCAT statements which also only fills one field (the assigned_user field) and making the other one null.
So what you need to do is JOIN the Users table again. One for the assigned, one for the create. Something like this...
SELECT `task_id`, `description`,`assessment`,
(SELECT CONCAT(`U1.first_name`, ' ',`U1.last_name`)) AS assigned_user,
(SELECT CONCAT(`U2.first_name`, ' ',`U2.last_name`)) AS creator_user,
`created_on`,`last_modified`,`status`
FROM `Tasks`
LEFT JOIN `Users` U1
ON Tasks.assigned_user_id = U1.user_id
LEFT JOIN `Users` U2
ON Tasks.creator_id = U2.user_id
WHERE task_id=2
Before you had an OR. It does not look at one side, look for the id, then look at the other one, look for the id, then use it at once. It is exactly that. If the current user_id it is looking for happens to be one of those two then it uses that single user_id.

You need to join to your users table twice and alias them...
Somethin like...
SELECT `task_id`, `description`,`assessment`,
(SELECT CONCAT(`assignedUsers.first_name`, ' ',`assignedUsers.last_name`) AS assigned_user,
(SELECT CONCAT(`createdUsers.first_name`, ' ',`createdUsers.last_name`) AS creator_user,
`created_on`,`last_modified`,`status`
FROM `Tasks`
LEFT JOIN `Users` assignedUsers ON Tasks.assigned_user_id = assignedUsers .user_id
LEFT JOIN `Users` createdUsers ON Tasks.creator_id = createdUsers .user_id
WHERE task_id=2

Since you have two foreign keys and you want to fetch the corresponding data you just have to INNER JOIN the users table twice.
You used LEFT JOIN which will fetch all the data from the first table,in your case tasks, even if no match is found in the second table (in your case this did not made any difference but in cases where an id is not set or the user does not exist anymore maybe this is a problem that is up to you to decide...) and you also used OR in the JOIN conditions which resulted in duplicate tasks in the results.
So you must INNER JOIN twice.One time to get the assigned user and one to get the creator.
Havent tested but this should work :
SELECT t.`task_id`, t.`description`,t.`assessment`,
CONCAT(u1.`firstname`,' ',u1.`lastname`) as creator,
CONCAT(u2.`firstname`,' ',u2.`lastname`) as assigned_user,
t.`created_on`,t.`last_modified`,t.`status`
FROM `tasks` t
INNER JOIN `users` u1 ON t.creator_id=u1.id
INNER JOIN `users` u2 ON t.assigned_user_id=u2.id
WHERE t.`task_id`=2

Thank you all guys but I fixed it by doing:
SELECT `task_id`, `description`,`assessment`,
(SELECT CONCAT(`first_name`, ' ', `last_name`)
FROM `Users`
WHERE `Tasks`.`assigned_user_id` = `Users`.`user_id`) AS assigned_user,
(SELECT CONCAT(`first_name`, ' ', `last_name`)
FROM `Users`
WHERE `Tasks`.`creator_id`=`Users`.`user_id`) AS creator_user,
`created_on`,`last_modified`,`status`
FROM `Tasks`
WHERE task_id=3
I just added FROM Users and WHERE clauses in each inner SELECT,
so I didn't have to do any joins... As always it was way more simple than I thought. Thanks again, much appreciated!

Related

The multi-part identifier could not be bound in a multiple condition where statement

I'm trying to add an additional WHERE condition with AND to my mySQL query but keep getting the error The multi-part identifier userR.externalSource could not be bound
I have the alias setup in my left join from my other table but still I get this error. Could someone explain me why this is happening. I found article but this does not seem to be the case. I think it's rather a formatting issue of my query.
This query just counts 2 different groups of users but they can't have an empty (NULL) field for the externalSource field from the dbo.AAA_users table.
SELECT SUM(A.ROLECOUNT) AS 'Advanced Users' ,
(SELECT SUM(A.ROLECOUNT)
FROM (
SELECT role.logicalName AS Role_name ,
COUNT(DISTINCT users.userId) AS RoleCount
FROM dbo.AAA_UserRoleResource AS users
LEFT JOIN dbo.AAA_Role AS role ON role.roleId = users.roleId
LEFT JOIN dbo.AAA_User AS userR ON userR.userId = users.userId
GROUP BY role.logicalName
) A
WHERE A.Role_name IN ('ROLE_VIEWER', 'ROLE_USER')
AND userR.externalSource is not NULL <-- issue here
) AS 'Basic user'
FROM(
SELECT role.logicalName AS Role_name ,
COUNT(DISTINCT users.userId) AS RoleCount
FROM dbo.AAA_UserRoleResource AS users
LEFT JOIN dbo.AAA_Role AS role ON role.roleId = users.roleId
LEFT JOIN dbo.AAA_User AS userR ON userR.userId = users.userId
GROUP BY role.logicalName
) A
WHERE A.Role_name IN ('ROLE_ADMIN');
Any help appreciated.
So i figured out why it was not working.
I was adding the additional WHERE condition outside of the parenthesis and because of this it did not find the identifier which is within the parenthesis.

How can I select a row count into a field for each row of the result set?

Sorry for the question (not for asking it, but for the way it's worded - I'm not... entirely certain how to ask it...).
I'm running the following SQL statement:
SELECT
`U`.`user_id` AS `User_ID`
/*A lot of irrelevant stuff*/
COUNT(`U`.`parent_id` = `U`.`user_id`) AS `SubAccounts_Active` #This is the part of the statement with which I'm having trouble.
/*More non-relevant stuff*/
FROM `users` AS `U`;
/*Non-relevant join.*/
The bit of SQL on which I commented (COUNT(U.parent_id = U.user_id)) I had initially as just 0.
I need, for each row, to count the number of rows in which the field parent_id matches the field user_id from the table users.
I thought the SQL I had would do the job but it flattens the result set into a single row and that won't work for me (I'd forgotten that COUNT and SUM both flatten the result set (been a while since I've done anything with SQL so I'm not sure what the technical term for that is, but I digress...)) - I need the total for each row (user) to be present within the defined field (SubAccounts_Active).
What SQL would I need to employ to achieve this result?
EDIT 1
Thanks to the suggestion I received in the first comment I was able to stumble my way through to the following SQL that returned the proper result set -
SELECT
`U`.`user_id` AS `User_ID`,
COUNT(`C`.`parent_id` = `U`.`user_id`) AS `SubAccounts_Active`
FROM `users` AS `U`
LEFT JOIN `users` AS `C`
ON `C`.`parent_id` = `U`.`user_id`
GROUP BY `U`.`user_id`;
This returned the result set I was expecting where the count of each parent accounts children was set in the SubAccounts_Active field.
You can use a LEFT JOIN and a count:
SELECT
`U`.`user_id` AS `User_ID`,
/* A lot of irrelevant stuff */
COUNT(U2.user_id) AS SubAccounts_Active
FROM
`users` AS `U` LEFT JOIN `users` AS `U2`
ON u.parent_id=u2.user_id
GROUP BY
`U`.`user_id`,
/* other fields */
U2.user_id will be null when the join doesn't succed, and non-null otherwise. COUNT will always count all of the rows where u.parent_id=u2.user_id.
http://sqlfiddle.com/#!9/f6707/2
SELECT
p.user_id AS User_ID,
COUNT(DISTINCT c.user_id) AS SubAccounts_Active
FROM users AS p
LEFT JOIN users c
ON p.user_id = c.parent_id
GROUP BY p.user_id

MySql update value with multitalbe select

I need to write MySql script to automatically change member role in Redmine bugtracker every week or so. The problem is that I got user login and there is a really long way to get to the member role id (what you can figure out by hte nested select in find_in_set function).
I made this query, which displays actually what I want, but the table is not updated:
SELECT REPLACE (role_id, '6', '4') as role_id
FROM member_roles
WHERE FIND_IN_SET( member_roles.member_id , (
SELECT member_roles.member_id
FROM members,users,member_roles, roles
WHERE members.id=member_roles.member_id
AND members.user_id=users.id
AND member_roles.role_id=roles.id
AND users.login='user01'))
the result of this query is:
-----------
| role_id |
-----------
| 4 |
-----------
I do not know how to update table instead of just displaying the result of this query.
Any help will be appreciated.
You just need to put that query in the update clause, for example:
UPDATE member_roles SET YOUR_FIELD(S)_TO_UPDATE=VALUE(S)
WHERE role_id=(YOUR_SELECT_CLAUSE)
You can change the operator to IN in the where clause in case you have multiple results
I converted the whole thing to the multi-table syntax for UPDATE:
UPDATE member_roles
JOIN members ON members.id = member_roles.member_id
JOIN users ON members.user_id = users.id
JOIN roles ON member_roles.role_id=roles.id
SET member_roles.role_id = '4'
WHERE member_roles.role_id = '6'
AND users.login='user01'
Note that I've added the condition that role_id = '6' in order to allow for your REPLACE() statement - I'm not sure if you want that or not.
I hope I got all your conditions correct - you may want to test it like this first:
SELECT role_id
FROM member_roles
JOIN members ON members.id = member_roles.member_id
JOIN users ON members.user_id = users.id
JOIN roles ON member_roles.role_id=roles.id
WHERE member_roles.role_id = '6'
AND users.login='user01'
Check and make sure you are getting the same number of records that you were getting before.
try this:
UPDATE member_roles
set role_id='4'
WHERE FIND_IN_SET( member_roles.member_id , (
SELECT member_roles.member_id
FROM members,users,member_roles, roles
WHERE members.id=member_roles.member_id
AND members.user_id=users.id
AND member_roles.role_id=roles.id
AND users.login='user01'))

MySQL - Update column values using subquery select itself

Please help me with MySQL update to update the column with result returns from select itself.
For instance, I have two tables
TABLE user(
userid int,
groupid int
)
TABLE thread (
threadid int,
userid int,
sticky tinyint,
vip tinyint
)
Now I'm trying to achieve this with a single update query, but can't seem to do it. What I thought I should do is:
UPDATE user SET groupid=15 WHERE userid IN (SELECT userid FROM thread t LEFT JOIN user u ON u.userid=t.userid WHERE (t.sticky=1 AND t.vip=1) AND (u.groupid=11 OR u.groupid=14) GROUP BY t.userid);
but MySQL saids: #1093 - You can't specify target table 'user' for update in FROM clause
Please help me!
It can be done by generating a new table from left join of two tables and then update from the filtered result, syntax will be as follows:
UPDATE user AS nu
INNER JOIN
(SELECT u.userid, u.groupid
FROM thread t LEFT JOIN user u
ON u.userid=t.userid
WHERE (t.sticky=1 AND t.vip=1) AND
(u.groupid=11 OR u.groupid=14)
GROUP BY t.userid) AS my_table
ON nu.userid = my_table.userid
SET nu.groupid = 15;
Try using the following:
UPDATE user u2 SET u2.groupid=15 WHERE u2.userid IN (SELECT userid FROM thread t LEFT JOIN user u ON u.userid=t.userid WHERE (t.sticky=1 AND t.vip=1) AND (u.groupid=11 OR u.groupid=14) GROUP BY t.userid);
This should do the trick, hope it helps :)
update user1 u
left join thread1 t on t.userid = u.userid
where (t.sticky=1 AND t.vip=1) AND (u.groupid=11 OR u.groupid=14)
set u.groupid = 15
GROUP BY t.userid;
Use this
Is your desired query materially different from this...
UPDATE user u
JOIN thread t
ON t.userid = u.userid
SET groupid = 15
WHERE t.sticky = 1
AND t.vip = 1
AND u.groupid IN(11,14);
?
If so, then as Rockse suggests, consider providin proper DDLs (and/or an sqlfiddle) so that we can more easily replicate the problem, together with a corresponding desired result set.
You can add this before your query:
use yourdatabase;
yourdatabase is database name which includes user table

Updating a Field of record if it is found to be a duplicate

I have the following Mysql select that returns all the duplicate email addresses in my table.
SELECT users.FirstName, `Surname`, users.email1
FROM users
INNER JOIN (
SELECT email1
FROM users
GROUP BY email1
HAVING count(email1) > 1) dup ON users.email1 = dup.email1
ORDER BY users.email1
This works great, what I wish to do now is update a field called users.DupEmail with "YES" but I am unsure how to do this. I have tried putting an update line to replace the select, but I am clearly getting it wrong as I am back here asking for help.
Can anyone help please?
You can just use the multiple-table UPDATE syntax with a self-join:
UPDATE users AS u1
JOIN users AS u2 USING (email1)
SET u1.DupEmail = 'YES'
WHERE NOT u1.id = u2.id; -- use your primary key here
The following script must solve your problem -- be safe and use the SELECT to test results before aplying the update:
-- SELECT FirstName, Surname, email1
-- FROM users
UPDATE users
SET DupEmail = 1
WHERE u0.email1 IN (
SELECT u1.email1
FROM users u1
GROUP BY u1.email1
HAVING COUNT(*) > 1
)