'How to do Group By and SELF JOIN in single table
Here is what I am trying to get done. Partly I am thru, Partly I am stuck, and need some help.
Table structure:
CREATE TABLE UserRole
(
Id bigint NOT NULL AUTO_INCREMENT,
Name varchar(100) NOT NULL,
Description varchar(200) NULL,
IsEnabled bit NOT NULL, -- 1 if a role is enabled, 0 otherwise
Created date NOT NULL, -- When a role was created
CreatedBy varchar(200) NOT NULL, -- Who created a role
Updated date NULL, -- When a role was updated (if at all)
UpdatedBy varchar(200) NULL, -- Who updated a role (if at all)
CONSTRAINT PK_UserRole PRIMARY KEY ( Id ASC )
)
Data:
| Id | Name | Description | IsEnabled | Created | CreatedBy | Updated | UpdatedBy |
|---|---|---|---|---|---|---|---|
| 1 | Role_1 | NULL | 1 | 2020-04-15 | Admin | NULL | NULL |
| 2 | Role_2 | Description | 1 | 2020-04-16 | ADMIN | 2020-04-17 | John Smith |
| 3 | Role_3 | Description | 0 | 2020-04-16 | John SMITH | 2020-04-17 | Ben SMITH |
| 4 | Role_4 | Description | 1 | 2020-04-19 | bEn SmiTh | 2020-04-21 | BEN SMITH |
Expected result and my working on getting the report:
To get UserName, NoOFCreatedRoles, NoOfCreatedEnabled and NoOfUpdated.
For the above input here is the sample output
A few assumptions:
You can assume there is at least one role created by each user.
Your query should work on MySQL 8.0 since I am using that and that is what is expected.
Query should not return NULL for numerical columns, instead of NULL get it replaced with -1.
My solution
-- Collect NoOfCreatedRoles ( Working )
select UCASE(CreatedBy), count(*) as 'NoOfCreatedRoles'
from UserRole
Group By UCASE(CreatedBy)
order by UCASE(CreatedBy) desc;
Result
| UCASE(CreatedBy) | NoOfCreatedRoles |
|---|---|
| JOHN SMITH | 1 |
| BEN SMITH | 1 |
| ADMIN | 2 |
-- Collect NoOfUpdatedRoles
select TRIM(UCASE(UpdatedBy)), count(*) as 'NoOfUpdatedRoles'
from UserRole
Group By TRIM(UCASE(UpdatedBy)) desc;
| TRIM(UCASE(UpdatedBy)) | NoOfUpdatedRoles |
|---|---|
| JOHN SMITH | 1 |
| BEN SMITH | 2 |
| NULL | 1 |
NULL should be removed, why is it coming ????
-- Joined Query
select distinct UCASE(a.CreatedBy) as 'UserName' ,
count(*) as 'NoOfCreatedRoles' ,
count(*) as 'NoOfUpdatedRoles' from UserRole a
join UserRole b
where TRIM(UCASE(a.CreatedBy)) = TRIM(UCASE(b.CreatedBy))
Group By UCASE(a.CreatedBy), UCASE(a.UpdatedBy)
order by UCASE(a.CreatedBy) desc;
| UserName | NoOfCreatedRoles | NoOfUpdatedRoles |
|---|---|---|
| JOHN SMITH | 1 | 1 |
| BEN SMITH | 1 | 1 (Wrong, should be 2) |
| ADMIN | 2 | 2 (Wrong, should be -1) |
Also, why am I getting the wrong result and how do I get numberOfCreatedAndEnabled column, need some help with some insightful descriptions would be greatly appreciated.
Solution 1:[1]
Count first, join the results
select t1.usr, NoOfCreatedRoles,
case NoOfCreatedEnabled when 0 then -1 else NoOfCreatedEnabled end NoOfCreatedEnabled,
coalesce(NoOfUpdatedRoles, -1) NoOfUpdatedRoles
from (
select TRIM(UCASE(CreatedBy)) usr, count(*) as NoOfCreatedRoles ,
sum(IsEnabled ) as NoOfCreatedEnabled
from UserRole ur
Group By TRIM(UCASE(CreatedBy))
) t1 left join (
select TRIM(UCASE(UpdatedBy)) usr, count(*) as NoOfUpdatedRoles
from UserRole
Group By TRIM(UCASE(UpdatedBy))
) t2 on t1.usr = t2.usr
Solution 2:[2]
Since IsEnabled column is 1 for all the enabled roles simply sum() will calculate NoOfCreatedEnabled. To calculate NoOfUpdatedRoles you can use subquery. Combined query will be as below:
Query 1:
select UCASE(CreatedBy) as 'UserName', count(*) as 'NoOfCreatedRoles',
sum(IsEnabled ) as 'NoOfCreatedEnabled ',
(select count(*) from UserRole u where UCASE(u.UpdatedBy) = UCASE(ur.CreatedBy) ) as 'NoOfUpdatedRoles'
from UserRole ur
Group By ur.CreatedBy
order by UCASE(CreatedBy) desc;
Output:
| UserName | NoOfCreatedRoles | NoOfCreatedEnabled | NoOfUpdatedRoles |
|---|---|---|---|
| JOHN SMITH | 1 | 0 | 1 |
| BEN SMITH | 1 | 1 | 2 |
| ADMIN | 2 | 2 | 0 |
Query 2 (replacing 0 with -1):
select UCASE(CreatedBy) as 'UserName', count(*) as 'NoOfCreatedRoles' ,
sum(IsEnabled ) as 'NoOfCreatedEnabled ',
(select COALESCE(NULLIF(count(*),0),-1) from UserRole u where UCASE(u.UpdatedBy) = UCASE(ur.CreatedBy) ) as 'NoOfUpdatedRoles'
from UserRole ur
Group By ur.CreatedBy
order by UCASE(CreatedBy) desc;
nullif(parameter1,0) will return null if parameter1 is same as second parameter which is 0 here. coalesce(parameter1,-1) will return -1 only if parameter1 is null
Output:
| UserName | NoOfCreatedRoles | NoOfCreatedEnabled | NoOfUpdatedRoles |
|---|---|---|---|
| JOHN SMITH | 1 | 0 | 1 |
| BEN SMITH | 1 | 1 | 2 |
| ADMIN | 2 | 2 | -1 |
db<>fiddle here
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Serg |
| Solution 2 | philipxy |

