Joining 3 tables - doing joins how to - sql

This is my current query - its not getting the required result. I want it do display all of the "resources" even if they dont have a connection.
SELECT *
FROM (`user_permissions`)
JOIN `user_groups` ON `user_groups`.`id` = `user_permissions`.`role`
JOIN `user_resources` ON `user_resources`.`id` = `user_permissions`.`resource`
WHERE `role` = '4'
When I try left join or right join it still returns the same result. The result I get is:
id | role | resource | name
5 | 4 | 2 | Changelog
I want
id | role | resource | name
5 | 4 | 2 | Changelog
null | null | null | Resource2
null | null | null | Resource3
Is this possible?

Looking through your query, role is part of user_permissions which is one of the connections that may or may not exist. Consider changing your where clause to WHERE `role`= '4' OR `role` IS NULL if you want to display these null records as well...
Also, although this can be accomplished by a right join, I believe it's more readable/understandable if you select from user_resources instead, then left join on the other tables. This follows from your problem description statement; you want to "display all of the 'resources' even if they don't have a connection", which means you want to select from resources, then join on any connections if they exist.

in your sql you ask for
WHERE `role` = '4'
and want an result that has null in role ? null is not 4, so there is no such result.

First: your where clause will always restrict the result set. You have a condition that requires role to be "4" -- so it doesn't display any results unless the result has "4" in the role column.
Second: where is "name" coming from -- since that is the one you want to appear, you need to have the table with that column as your base table and LEFT JOIN the other two tables.
Assuming "name" is coming from resources, your query should read:
SELECT *
FROM `user_resources`
LEFT JOIN `user_permissions` ON `user_permissions`.`resource` = `user_resources`.`id`
LEFT JOIN `user_groups` ON `user_groups`.`id` = `user_permissions`.`role`

SELECT *
FROM (user_permissions)
JOIN user_groups ON user_groups.id = user_permissions.role
JOIN user_resources ON user_resources.id = user_permissions.resource
WHERE (role = '4' or role is null)
you will get result from this query

Related

SQL Join using a Junction Table not returning expected results

I'm joining 3 tables in SQL Server for the purpose of mapping users to their managers. The purpose of the junction table is to map their full usernames to their network ID. Here's the query:
select
dbo.Employee_Records.EMPLOYEE_NAME as AD_NAME,
dbo.Employee_Records.NAME as NAME,
dbo.Employee_Records_MANAGER_S_.KEYWORD as SUPERVISOR_FULLNAME,
dbo.employee_records.EMPLOYEE_NAME as SUPERVISOR_ADNAME
from
dbo.Employee_Records
left outer join dbo.Employee_Records_MANAGER_S_ on
dbo.Employee_Records.ID = dbo.Employee_Records_MANAGER_S_.ID
left join dbo.Employee_Records_MANAGER_S_ as ManagersList on
ManagersList.KEYWORD = dbo.employee_records.name
The first 3 columns appear as I'd expect, showing the Network ID, Full User Name and the Manager's Full Name. However, the 4th column is the issue. It's showing the network ID of the user, not the manager.
It appears like this:
AD_NAME | NAME | SUPERVISOR_FULLNAME | SUPERVISOR_ADNAME
USER1 | User, Test | Supervisor, Test | USER1
USER1 | User, Test | Supervisor2, Test | USER1
It should appear like this:
AD_NAME | NAME | SUPERVISOR_FULLNAME | SUPERVISOR_ADNAME
USER1 | User, Test | Supervisor, Test | SUPERVISOR1
USER1 | User, Test | Supervisor2, Test | SUPERVISOR2
The Employee.Records table contains all full usernames and full supervisor names. The Employee.Records_MANAGER_S_ table is used to tie the Supervisors to the users, since each user could have multiple supervisors. All of the mappings for Network Names and Full Names are also in the Employee.Records table, so technically I'm trying to join the Employee.Records table to the Employee_Records_MANAGER_S_ and then do another join of the Employee_Recors_MANAGER_S_ back to the Employee.Records table again, but this time joining on the SUPERVISOR_FULLNAME instead of the Employee's name.
If I understand correctly you need to join your dbo.Employee_Records table on a second time to get the supervisor details.
select
ER.EMPLOYEE_NAME as AD_NAME,
ER.[NAME] as [NAME],
M.KEYWORD as SUPERVISOR_FULLNAME,
MR.EMPLOYEE_NAME as SUPERVISOR_ADNAME
from dbo.Employee_Records as ER
left outer join dbo.Employee_Records_MANAGER_S_ as M on ER.ID = S.ID
left join dbo.Employee_Records as MR on MR.[NAME] = M.KEYWORD;
Note: As shown I highly recommend short table aliases as they make the query much clearer.

In Oracle SQL is there a way to join on a value twice?

Lets say I have two tables, with the following columns:
cars
motorcycle_id | fuel_id | secondary_fuel_id ...
fuel_types
fuel_id | fuel_label | ...
In this case fuel_id and secondary fuel_id both refer to the fuel_types table.
Is it possible to include both labels in an inner join? I want to join on the fuel_id but I want to be able to have the fuel label twice as a new column. So the join would be something like:
motorcycle_id | fuel_id | fuel_label | secondary_fuel_id | secondary_fuel_label | ...
In this case I would have created the secondary_fuel_label column.
Is this possible to do in SQL with joins? Is there another way to accomplish this?
You would just join twice:
select c.*, f1.fuel_label, f2.fuel_label as secondary_fuel_label
from cars c left join
fuel_types f1
on c.fuel_id = f1.fuel_id left join
fuel_types f2
on c.fuel_id = f1.secondary_fuel_id ;
The key point here is to use table aliases, so you can distinguish between the two table references to fuel_types.
Note that this uses left join to be sure that rows are returned even if one of the ids are missing.

GROUPBY many to many with itself

I have a User table that has a many to many relation with itself, and I would like to get all the pairs of users with this specific relation. The problem is, in the relation table I store users like that:
+------+---------------+
| User | relation |
+------+---------------+
| id | left_user_id |
| name | right_user_id |
| ... | ... |
+------+---------------+
So when I do a basic
SELECT count(*)
FROM relation LEFT OUTER JOIN user AS user_1 ON user_1.id = relation.left_user_id
LEFT OUTER JOIN user AS user_2 ON user_2.id = relation.right_user_id
GROUP BY left_user_id, right_user_id;
I sometimes get two results for the same pair (for example sometimes (Adam, Eva) and (Eva, Adam) which are the same pair). What I would like to achieve is just one pair: (Adam, Eva).
How can this be achieved?
You can use the functions least() and greatest():
SELECT count(*)
FROM relation r
LEFT OUTER JOIN user AS user_1 ON user_1.id = r.left_user_id
LEFT OUTER JOIN user AS user_2 ON user_2.id = r.right_user_id
GROUP BY LEAST(r.left_user_id, r.right_user_id), GREATEST(r.left_user_id, r.right_user_id);
Or in this case where you don't need the joins:
SELECT count(*)
FROM relation
GROUP BY LEAST(left_user_id, right_user_id), GREATEST(left_user_id, right_user_id);
The left joins should not be necessary. The key is simply using least() and greatest(). That would be:
SELECT LEAST(r.left_user_id, r.right_user_id) as user_id_1,
GREATEST(r.left_user_id, r.right_user_id) as user_id_2,
COUNT(*)
FROM relation
GROUP BY user_id_1, user_id_2;
The one caveat with this approach is that the pair in the result set may not be in the original data -- in that order. So, if you had "Eve"/"Adam" once in the data, then it would return: "Adam"/"Eve"/1. That can be addressed, if necessary.

Select data from three different tables with null data

I am new in Sql. My question is how to get data from three different tables with null values.
I have tried a query as below:
SELECT *
FROM [USER]
JOIN [Location] ON ([Location].UserId = [USER].Id)
JOIN [ParentChild] ON ([ParentChild].UserId = [USER].Id) WHERE ParentId=7
which I find from this link.
Its working fine but, it not fetches all and each data associated with the ParentId
Something like it only fetches data which are available in all tables, but also omits some data which not available in Location tables but it comes under the given ParentId.
For example:
+----------+-------------+
| UserId | ParentId |
+----------+-------------+
| 1 | 7 |
+----------+-------------+
| 8 | 7 |
+----------+-------------+
For userId 8, there is data available in Location table,so it fetches all data. But there is no data for userId 1 available in Location table, so the query didn't work for this.
But I want all and every data.
If there is no data for userId then it can return only null columns.
Is it possible ??
hope everyone can understand my problem.
If you always want to return a list of users, but some may not have locations then you want to change the type of join from an "Inner Join" (or as you have used the short hand "JOIN") to a "Left Join".
SELECT *
FROM [USER]
INNER JOIN [ParentChild] ON ([ParentChild].UserId = [USER].Id)
LEFT JOIN [Location] ON ([Location].UserId = [USER].Id)
WHERE ParentId=7
This doesn't account for users that do not have a parent. If you still want to return users who do not have a parent then you would then need to change the JOIN type to your ParentChild table to a LEFT join also.

What kind of SQL join do I need to compress a One to Many relationship into the same view row?

Edit: this isn't to be a dynamic output, the output view structure is fixed.
I am trying to create a SQL Server view that shows a single fixed column row for each user, and flattens out an associated one to many table into that row.
Although the associated table has a one to many relationship, the output table structure is limited to 4 elememts form that table.
My table structure is like so:
User (Id, FirstName, LastName)
Assessment (Id, Date, Location, User_Id)
Topics (Id, Topic, Assessment_Id)
Where the Assessment is joined to the User by the User_Id (One 2 One), and the Topics are joined to the Assessment by the Assessment_Id.
So, if I have three topics for an assessment, I'd want the view to look something like:
User_Id | FirstName | LastName | Date | Location | Topic1 | Topic2 | Topic3 | Topic4 |
1 | dave | toby | 2/2/11 | In situ | apples | pears | lemons | NULL |
My current SQL looks like this:
SELECT User.Id, User.FirstName, User.LastName, Assessment.Date, Assessment.Location, Topic.Topic
FROM User LEFT OUTER JOIN
Assessment INNER JOIN
Topic ON Assessment.Id = Topic.Assessment_Id ON
User.Id = Assessment.User_Id
But this returns a row for each concern - it doesn't compress them to one line. I've played with a few different joins, but haven't been able to get the behaviour I want.
Is it possible to do this in a view?
What do I need to do to make it happen??
Thanks!
There is no such JOIN. SQL has a fixed column output: so you can't add arbritrary numbers of columns. It doesn't matter if it's a view, direct or in a stored procedure.
There are 2 main options
concatenate the many rows into one column which is a popular questions here on SO. One random solution using XML PATH
use dynamic SQL to add a column per row in a stored procedure.
Note: PIVOT is fixed column output too
Edit: for a maximum of 4 child rows
SELECT
P.col1, P.col2,
C1.col1 AS Topic1,
C2.col1 AS Topic2,
C3.col1 AS Topic2,
C4.col1 AS Topic4
FROM
Parent P
LEFT JOIN
Child C1 ON P.Key = C1.FKey AND C1.ID = 1
LEFT JOIN
Child C2 ON P.Key = C2.FKey AND C2.ID = 2
LEFT JOIN
Child C3 ON P.Key = C3.FKey AND C3.ID = 3
LEFT JOIN
Child C4 ON P.Key = C4.FKey AND C4.ID = 4
You can use PIVOT too but I prefer the simpler self joins.
Take a look at PIVOT table functionality - e.g. http://www.help-sql.info/27/9/610208.html and http://blog.sqlauthority.com/2008/05/22/sql-server-pivot-table-example/
Although you will need to know the AssessmentId's before you can write the PIVOT