'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 |