1

I have 2 tables: Users and Roles.

Users table columns:

UserId  FirstName  Lastname

Roles table columns:

RoleId  UserId  ParentId

I want to be able to fetch date from these 2 tables representing a column named ParentName.

So lets say I have these data:

Users table:

UserId  FirstName  Lastname
1       John       Doe
2       Jane       Smith
3       John       Smith

Roles table:

RoleId  UserId  ParentId
1       1       NULL
2       2       1
3       3       2

So I want to have this table:

UserId  FirstName  Lastname   RoleId   ParentId   ParentName 
1       John       Doe        1        NULL       NULL
2       Jane       Smith      2        1          John Doe
3       John       Smith      3        2          Jane Smith

I tried but I couldn't. I tried INNER JOIN, OUTER JOIN, Subqueries but I Couldn't get what I wanted.

In joining 2 tables together I have this:

SELECT UserId, FirstName, Lastname, RoleId, ParentId
FROM Users INNER JOIN Roles ON Users.UserId = Roles.UserId

And In joining a self recursive Roles table I have this:

SELECT ChildUsers.UserId, ChildUsers.RoleId, ParentUsers.UserId, ParentUsers.RoleId, Users.LastName
FROM Roles AS ChildUsers
LEFT JOIN Roles AS ParentUsers ON ChildUsers.ParentId = ParentUsers.RoleId 
INNER JOIN Users ON Users.UserId = ParentUsers.UserId

But I couldn't get what I wanted. How can I get that?

-----------UPDATE---------------

Here is my own solution:

SELECT Roles.RoleID, LastName, Parent.ParentName FROM
Roles INNER JOIN Users ON Users.UserID = Roles.UserID LEFT JOIN
(SELECT LastName AS ParentName, RoleID FROM Users INNER JOIN Roles ON Users.UserID = Roles.UserID) Parent ON Parent.RoleID = Roles.ParentID

But I choose LukStorms solution as the answer.

2
  • Which DBMS are you using? Postgres? Oracle? Commented Apr 25, 2017 at 9:37
  • @a_horse_with_no_name SQL SERVER Commented Apr 25, 2017 at 10:53

4 Answers 4

1

To get that expected result?

If the ParentId contains RoleId's then this should work:

SELECT 
  r.UserId, 
  u.FirstName, 
  u.Lastname, 
  r.RoleId, 
  r.ParentId, 
  pu.FirstName+' '+pu.LastName AS ParentName 
FROM Roles r
LEFT JOIN Users u ON r.UserId = u.UserId
LEFT JOIN Roles pr on r.ParentId = pr.RoleId
LEFT JOIN Users pu on pr.UserId = pu.UserId

But if the ParentId would be a foreign key to the Users table:

SELECT 
  r.UserId, 
  u.FirstName, 
  u.Lastname, 
  r.RoleId, 
  r.ParentId, 
  p.FirstName+' '+p.LastName AS ParentName
FROM Roles r
LEFT JOIN Users u ON r.UserId = u.UserId
LEFT JOIN Users p on r.ParentId = p.UserId
Sign up to request clarification or add additional context in comments.

5 Comments

Thank you but with your code, In the result set, the ParentName column for all rows are NULL
Is the FirstName or the LastName in the Users table sometimes NULL? Because I tried it with your test input and it works. I ask this because 'text' + NULL gives NULL. Also, is the ParentId a foreign key to the Users table, or to the Id of the Roles table?
None of the FirstNames and LastNames are NULL and ParentId refers to the id of the Roles table.
Thank you so much. Now It's working! and I came to put my solution and edit my post and I saw your updated answer. I'm gonna choose your answer and update my post with my solution. thanks a lot!
Good that this helped. Maybe you could change the numbers of the RoleId's and ParentId's in your question, so that they are different from the UserId's. That makes it more obvious what's in those ParentId's. :)
0

Using this query you can get the result you want. Since you only want a 2 level depth, you can do it without recursive queries

DECLARE @Users TABLE (UserId INT, FirstName VARCHAR(255), LastName VARCHAR(255))
INSERT INTO @Users VALUES
(1,'John','Doe'),
(2,'Jane','Smith'),
(3,'John','Smith')

DECLARE @ROles TABLE (RoleId INT, UserId INT, ParentId INT)
INSERT INTO @ROles VALUES
(1,1,NULL),
(2,2,1),
(3,3,2)

SELECT  u.UserId, u.FirstName, u.LastName, r.RoleId, r.ParentId, p.ParentName
FROM    @Users AS u
    LEFT OUTER JOIN @Roles AS r ON r.UserId = u.UserId
    LEFT OUTER JOIN
        (SELECT r.UserId AS ParentId, up.FirstName + ' ' + up.LastName AS ParentName
         FROM   @ROles AS r 
            INNER JOIN
                @Users AS up ON up.UserId = r.ParentId) AS p ON p.ParentId = u.UserId

Update: add result

UserId  FirstName LastName  RoleId  ParentId ParentName
1       John      Doe       1       NULL     NULL
2       Jane      Smith     2       1        John Doe
3       John      Smith     3       2        Jane Smith

4 Comments

Thank you but again, with your code, In the result set, the ParentName column for all rows are NULL
Hi, I added the result I get when executing the query in SSMS. As you can see the parentname is filled in. Could you check it again please ...
Thank you again but I'm still getting NULL values.
@pershianix I don't see the problem. If I run the query from LukStorms that you accepted, I'm getting the same result as with the query I wrote, where the parentname is filled in. Don't get it why you don't see it with my answer, while I do. Nevertheless glad you found an answer.
0

Try this query:-

Create table Users (UserId INT, FirstName VARCHAR(255), LastName 
VARCHAR(255));
INSERT INTO Users VALUES(1,'John','Doe');
INSERT INTO Users VALUES(2,'Jane','Smith');
INSERT INTO Users VALUES(3,'John','Smith');

Create table Roles(RoleId INT, UserId INT, ParentId INT);
INSERT INTO ROles VALUES(1,1,NULL);
INSERT INTO ROles VALUES(2,2,1);
INSERT INTO ROles VALUES(3,3,2);

SELECT a.*,concat(b.FirstName,' ',b.Lastname) as ParentName
from 
(
SELECT a.UserId, FirstName, Lastname, RoleId, ParentId
FROM Users a INNER JOIN Roles b ON a.UserId = b.UserId
) a
left join
Users b
on a.ParentId=b.userid;

Output:-

    UserId  FirstName   Lastname    RoleId  ParentId    ParentName
1   1           John    Doe            1    NULL     
2   2           Jane    Smith          2    1            John Doe
3   3           John    Smith          3    2            Jane Smith

5 Comments

I used your code but I get "The multi-part identifier "Users.UserId" could not be bound."
@ pershianix fixed it now, forgot to put alias. sorry for that
@pershianix did you try this query again?
Yes and now I'm getting NULL in the ParentName column
@pershianix it's working for me. I have pasted the full query which i wrote in sql server with output. There is absolutely no flaw in logic
0

Code explained:

CTE to get the user details and roles.

LEFT JOIN with the CTE to get the parent's information. Joining by the user's ParentId with the Parents's (who is also a user) UserId to get the ParentName.

(Note: You can run the cte separtely without the second join to see the output. Then run the whole to see how it is joining.)

CREATE TABLE [dbo].[Roles](
    [RoleId] [int] NULL,
    [UserId] [int] NULL,
    [ParentId] [int] NULL
)
;
CREATE TABLE [dbo].[Users](
    [UserId] [int] NULL,
    [FirstName] [varchar](20) NULL,
    [Lastname] [varchar](20) NULL
);



INSERT [dbo].[Roles] ([RoleId], [UserId], [ParentId]) VALUES (1, 1, NULL);
INSERT [dbo].[Roles] ([RoleId], [UserId], [ParentId]) VALUES (2, 2, 1);
INSERT [dbo].[Roles] ([RoleId], [UserId], [ParentId]) VALUES (3, 3, 2);

INSERT [dbo].[Users] ([UserId], [FirstName], [Lastname]) VALUES (1, N'John', N'Doe');
INSERT [dbo].[Users] ([UserId], [FirstName], [Lastname]) VALUES (2, N'Jane', N'Smith');
INSERT [dbo].[Users] ([UserId], [FirstName], [Lastname]) VALUES (3, N'John', N'Smith');


with cteUsers AS
(
    select u.UserId, u.FirstName, u.Lastname, r.RoleId, r.ParentId
    from Users AS u
    inner join Roles AS r
        on u.UserId=r.UserId
)
select 
     cteUsers.UserId
    ,cteUsers.FirstName 
    ,cteUsers.Lastname  
    ,cteUsers.RoleId    
    ,cteUsers.ParentId
    ,(p.FirstName + ' ' + p.Lastname) AS ParentName
from cteUsers
left join Users as p
    on cteUsers.ParentId=p.UserId
;

Run this script live

4 Comments

Thank you. I get these error messages: " The multi-part identifier "p.FirstName" could not be bound. The multi-part identifier "p.Lastname" could not be bound."
If were trying to run the CTE portion only then you need to comment the part (p.FirstName + ' ' + p.Lastname) AS ParentName. There was a comment line before --second join. I have removed it to make the code cleaner. Copy the code now and it should run without error.
Thank you , but now I get NULL values for all ParentNames.
Sorry, I have no idea why it would show NULL values for all ParentName. I just ran the query again it gave me all the ParentNames.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.