SQL join one table against multiple columns - sql

I am having 2 tables in my DB
1) Report table
AppNo AppName AddedBy AssignedTo ModifiedBy
-------------------------------------------
1 App1 1 2 1
2 App2 1 2 2
3 App3 2 2 2
4 App4 1 2 3
2) Users table
UserId UserName Role
----------------------
1 Raj Manager
2 Sid Lead
3 KK Rep
So i want to join two tables so that i can get names in place of Id's
Result Needed:
AppNo AppName AddedBy AssignedTo ModifiedBy
-------------------------------------------
1 App1 Raj Sid Raj
2 App2 Raj Sid Sid
3 App3 Sid Sid Sid
4 App4 Raj Sid KK
My Query:
SELECT
R.AppNo, R.AppName,
u1.UserName as AddedBy,
u2.UserName as AssignedTo,
u3.UserName as ModifiedBy
FROM Report R
LEFT OUTER JOIN Users u1 on u1.UserID = R.AddedBy
LEFT OUTER JOIN Users u2 on u2.UserID = R.AssignedTo
LEFT OUTER JOIN Users u3 on u3.UserID = R.ModifiedBy
But i dont want to join multiple time with User table..
As in my original report table there are nearly 8 UserId columns are there so i cant join 8 times it reduces performance.
Can anyone suggest the best way.
Note: I cant change table schema
Thanks in advance

Joining multiple times is the only way; that is what the data and structure is.
There should be no performance issue, as the database is typically well designed to handle this. Just because a JOIN looks complex to humans doesn't mean it is complex or expensive for the optimizer or the database.

You could use a cartesian join for the columns, although performance will depend heavily on how well indexed the tables are...
select R.AppNo,
R.AppName,
(select UserName from Users where UserID = R.AddedBy) as AddedBy,
(select UserName from Users where UserID = R.AssignedTo) as AssignedTo,
(select UserName from Users where UserID = R.ModifiedBy) as ModifiedBy
from Report R

Related

SQL Self Join + Another Join Not working

I've got two tables, Job and User, and am trying to pull together some records. Job needs to be joined with User, and User to itself. The structure is:
JobID | OwnerID | ClientName
1 | 1 | Corey
-----------------------------
UserID | ManagerID | Name | Email
1 | 2 | Jon Smith | job#test.com
2 | | Jane Doe | jane#test.com
I'm looking to obtain a table that contains the manager's email address, in addition to the user's name. My query is:
SELECT
Job.ClientName as 'ClientName',
U1.Name as 'SalesPersonName',
U2.Email as 'ManagerEmail'
FROM
Job
INNER JOIN User U1 ON Job.OwnerID = U1.UserID
INNER JOIN User U2 ON U1.UserID = U2.ManagerID
WHERE
Job.ID = '1'
This pulls all Job data, and all data for U1...but is failing to populate U2 - the manager's information.
I imagine I've got something messed up with the self-join. The query validates, so I'm at a loss for what it is.
Any guidance is greatly appreciated.
Note: the above table/column names have been simplified to protect the innocent.
It looks like you juxtaposed the table aliases on the fields in your last JOIN:
This: FULL JOIN User U2 ON U1.UserID = U2.ManagerID
Should be this: FULL JOIN User U2 ON U2.UserID = U1.ManagerID
Full query:
SELECT
Job.ClientName as 'ClientName',
U1.Name as 'SalesPersonName',
U2.Email as 'ManagerEmail'
FROM
Job
FULL JOIN User U1 ON Job.OwnerID = U1.UserID
FULL JOIN User U2 ON U2.UserID = U1.ManagerID
WHERE
Job.ID = 1 --removed ticks here, assuming int column
Note: You don't need a FULL JOIN unless there are records that won't join together, and you want to preserve those records with NULL values in your result set. For the example provided, a basic INNER JOIN would work, but maybe you need the FULL JOIN for your true data set so I left it in.
Note 2: user is a reserved word in many DBMS and could cause you headaches if used as a tablename

Self join through junction table?

I have a list of users, and a junction table where both fields are user_id, which represents a friendship.
So the database is
users
id | name
----------------------
1 Julia
2 Davos
3 Michael
4 Anthony
friendships
user1_id | user2_id
--------------------------
3 1
2 4
I want to select a list of the friendships, like
Michael | Julia
Davos | Anthony
But this one is just totally stumping me, and Google isn't helping. Can anyone offer a tip?
For get that information you should join twice to user table using friendship like pivot
SELECT friend_1.name,friend_2.name
FROM friendships AS relation_friend
JOIN users AS friend_1 ON friend_1.id=relation_friend.user1_id
JOIN users AS friend_2 ON friend_1.id=relation_friend.user1_id;
Regards.
It is dynamically working fine for any number of data:
select tbl.name, tb2.name from
(select *
from
users as a
inner join
friendships as b
on a.id=b.user1_id
or a.id=b.user2_id) as tbl
inner join
(select *
from
users as a
inner join
friendships as b
on a.id=b.user1_id
or a.id=b.user2_id) as tb2
on tbl.user2_id=tb2.id
where tbl.name <> tb2.name

Given a specific user I want to find all users having at least all of the roles this specific user has

Suppose I have user_role table having user and role columns.
| User | Role |
---------------
| 100 | 10 |
| 101 | 10 |
| 101 | 11 |
| 102 | 11 |
I want to write a query that will return users with same or lesser roles. For example:
Return user 100 for user 100
Return user 100,101,102 for user 101
Return user 102 for user 102
Business requirement: Suppose User X belongs to Asia group only. So X should have access permission to users who belongs to Asia group only. But say Y belongs to Asia and Europe groups. So Y should have access permission to users who belongs to:
Asia group only
Europe group only
Asia and Europe group only
Now, X should not access the data of Y as X does not belong to all the groups that Y belongs to. Similarly, say Z belongs to Asia, Europe and America. So, Z should access all the data of X, Y and Z but the reverse is not true.
My initial SQL:
select distinct(user) from user_role where role in
(select role from user_role where user=?);
Above query returns all the users sharing at least one common groups and not all common groups.
Can anybody please help me with a SQL example?
This can be done with much more less effort. The idea is left join roles on roles of particular user and then filter only those users for which all roles are found in that particular user's roles:
;with c as(select roleid from userroles where userid=100)
select r.userid from userroles r left join c on r.roleid = c.roleid
group by r.userid
having sum(case when c.roleid is null then 1 else 0 end) = 0
Fiddle http://sqlfiddle.com/#!6/bca579/7
Try this:
-- Create a CTE that will help us know the number of roles any user have.
;WITH CTE (UserId, RoleId, NumberOfRoles)
AS (
SELECT T1.UserId, RoleId, NumberOfRoles
FROM UsersToRoles T1 INNER JOIN
(
-- Derived table is needed so that we can have
-- the user, he's roleId and he's number of roles in the CTE
SELECT UserId, COUNT(RoleId) As NumberOfRoles
FROM UsersToRoles
GROUP BY UserId
) T2 ON(T1.UserId = T2.UserId)
)
-- We join the CTE with itself on the RoleId to get only users that have the same roles,
-- and on the NumberOfRoles to ensure that the users we get back have at least the nomber of roles as the user we testing.
SELECT DISTINCT T1.UserId
FROM CTE T1
INNER JOIN CTE T2 ON(T1.RoleId = T2.RoleId AND T1.NumberOfRoles <= T2.NumberOfRoles)
WHERE T2.UserId = #UserId
Play with it yourself in this sql fiddle
CTE, or Common Table Expressions is a concept introduced in Sql Server 2008. basically, you define a select statement that the rest of your sql can refer to as if it was a view.
In this case, you could have this CTE written as a view and it would give you the same result.

SQL replace values in selection many columns

I have two tables:
Table Users
UserId Name
1 John
2 Alice
3 Tom
4 Charles
....
23120 Bob
and
Table Clients
Id Name1 Name2 Name3 .... Name2345
1 1 3 450 4
2 2 17 33 1
...
15302920 44 231 5 7
I would like to make a SQL query where for
Clients.Id = 1
the results are like:
Id Name1 Name2 Name3 ... Name2345
1 John Tom Bill Charles
My clients table has thousands of columns and I try to find a way to replace the values in all columns of the selected results (except the ID column) without thousands of joins.
I know that I can use a query of the following type (but I would like to avoid thousands of joins):
SELECT a.ID,
b.name name1,
c.name name2,
d.name name3
FROM clients a
INNER JOIN users b
ON a.name1 = b.userID
INNER JOIN users c
ON a.name2 = c.userID
INNER JOIN users d
ON a.name3 = d.userID
WHERE a.ID = 1
In addtion:
I can't change the data/structure of the "Clients" table but I can change the "Users" table as necessary.
I can't duplicate the "Clients" table as well since it very large and changes rapidly.
The "Clients" table has thousands of columns and millions of rows, but the selected part is always a small enough subset of the table.
Is it possible?
There is no simple query to achieve this since SQL is fundamentally flexible in rows (vertically) and rigid in columns. This is the closest I came up with:
select c.id,
(select name from dbo.users where UserId = c.name1),
(select name from dbo.users where UserId = c.name2),
(select name from dbo.users where UserId = c.name3),
(select name from dbo.users where UserId = c.name4)
from clients c

SQL Query to display heirarchical data

I have two tables - 'Users' and 'Supervision'
For this example, my users table is very simple:-
Users
=====
ID (PK)
UserName
Some users manage other users, so I've built a second table 'Supervision' to manage this:-
Supervision
===========
UserID
SuperID - this is the ID of the staff member that the user supervises.
This table is used to join the Users table to itself to identify a particular users supervisor. It might be that a user has more than one supervisor, so this table works perfectly to this end.
Here's my sample data in 'Users':-
userID userName
1 Bob
2 Margaret
3 Amy
4 Emma
5 Carol
6 Albert
7 Robert
8 Richard
9 Harry
10 Arthur
And my data in 'Supervision':-
userID superID
1 2
1 3
2 4
2 5
3 4
3 5
6 1
6 7
7 8
7 9
9 10
If I want to see who directly reports to Bob, writing an SQL query is straightforward, and tells me that Margaret and Amy are his direct reports.
What I want to do however is to write a query that shows everybody who comes under Bob, so it would need to look at Bobs direct reports, and then their direct reports, and so on - it would give Margaret, Amy, Emma and Carol as the result in this case.
I'm assuming this requires some kind of recursion but I'm completely stuck..
You should use recursive CTE:
WITH RCTE AS
(
SELECT * FROM dbo.Supervision WHERE UserID = 1
UNION ALL
SELECT s.* FROM dbo.Supervision s
INNER JOIN RCTE r ON s.userID = r.superID
)
SELECT DISTINCT u.userID, u.userName
FROM RCTE r
LEFT JOIN dbo.Users u ON r.superID = u.userID
SQLFiddle DEMO
Sounds to me like you need a Recursive CTE. This article serves as a primer, and includes a fairly similar example to the one you have:
http://blog.sqlauthority.com/2012/04/24/sql-server-introduction-to-hierarchical-query-using-a-recursive-cte-a-primer/
Hope it helps.
WITH MyCTE
AS (
-- ID's and Names
SELECT SuperID, ID
FROM Users
join dbo.Supervision
on ID = dbo.Supervision.UserID
WHERE UserID = 1
UNION ALL
--Who Manages who...
SELECT s.SuperID, ID
FROM Supervision s
INNER JOIN MyCTE ON s.UserID = MyCTE.SuperID
WHERE s.UserID IS NOT NULL
)
SELECT distinct MyCTE.ID, NAMES.UserName, '<------Reports to ' as Hierarchy, res_name.UserName
FROM MyCTE
join dbo.Users NAMES on
MyCTE.ID = NAMES.ID
join dbo.Users res_name
on res_name.ID = MyCTE.SuperID
order by MyCTE.ID, NAMES.UserName, res_name.UserName