joining two tables - sql

I have users table. There are three other tables: developers, managers, testers. All of these tables have a foreign key user_id.
I need to find all the users who are either developer or manager. What the sql will look like?
Update: Someone can be both a developer and a manager.

One way to do it would be
SELECT u.*, 'Developer'
FROM users u
INNER JOIN developer d ON d.user_id = u.user_id
UNION ALL
SELECT u.*, 'Manager'
FROM users u
INNER JOIN manager m ON m.user_id = u.user_id

SELECT *
FROM users u
WHERE EXISTS
(
SELECT NULL
FROM developers
WHERE user_id = u.id
UNION ALL
SELECT NULL
FROM managers
WHERE user_id = u.id
)

SELECT u.*,
CASE d.user_id IS NULL THEN 'N' ELSE 'Y' END is_developer,
CASE m.user_id IS NULL THEN 'N' ELSE 'Y' END is_manager
FROM users u -- all users
LEFT JOIN developers d -- perhaps a developer
ON u.user_id = d.user_id
LEFT JOIN manager m -- perhaps a manager
ON u.user_id = m.user_id
WHERE d.user_id IS NOT NULL -- either a developer
OR m.user_id IS NOT NULL -- or a manager (or both)

SELECT
user_id
/* ...other desired columns from the user table... */
FROM
user
WHERE
user_id IN (SELECT user_id FROM developer UNION SELECT user_id FROM manager)
Here I am using IN rather than EXISTS so that the developer and manager tables only need to be queried one time. It's possible that the optimizer may do this anyway, but this makes it explicit.
Also, this solution does not return duplicates for users who are both managers and developers.

Related

Tricky query in DB2

I have a DB2 table with Users and another one with Groups.
This both tables are connected by ID.
At the table Groups I got many lines for a single user, like:
ID
Group
Jhonn
Admin
Jhonn
Common
Jhonn
RH
I'm trying to know all users from the table Users that are not in the Admin group at the Group table.
I'm doing the following:
SELECT ID FROM USER u
JOIN GROUPS g
ON u.ID = g.ID
WHERE g.GROUP NOT IN ('Admin')
But this query is giving me
ID
Group
Jhonn
Common
Jhonn
RH
How can I make a query to know if the user is not with the Admin group?
Assuming, based on your requirements that Jhonn shouldn't appear:
SELECT ID FROM USER u
JOIN GROUPS g
ON u.ID = g.ID
WHERE u.ID NOT IN (SELECT ID FROM GROUPS WHERE GROUP = 'Admin')
And depending on what columns you need from the tables, you could drop the join.
SELECT ID
FROM USERS U
WHERE NOT EXISTS
(
SELECT 1 FROM GROUPS G WHERE U.ID=G.ID AND G.GROUP='Admin'
)
Could you please try this
You can use LEFT OUTER JOIN and test for non-existence of a group:
SELECT u.ID
FROM USER u
LEFT OUTER JOIN GROUPS g
ON (u.ID = g.ID AND g.group = 'Admin')
WHERE g.id IS NULL;
db<>fiddle here

SQL Querying Incomplete Result

I am having trouble understanding what is wrong with this query or how to properly join it together.
I know that I am missing results from other queries that show both sides of the connections separately using two aliases for the same table.
Pretty much a friendship has two "id"s (source and target) which both map to the same column in the users table.
I believe it is the OR statement that is causing the invalid joining, and if so how is the joining suppose to be done. If not what is the problem?
SELECT DISTINCT u.id, u.first_name, u.last_name
FROM friendships AS f, users AS u
WHERE f.mutual = 'true' -- both are friends
AND (u.id = f.source_id OR u.id = f.target_id) -- this line?
AND f.created_at BETWEEN '2016-01-20' AND '2016-01-27' -- time period they became friends
GROUP BY u.id, u.first_name
ORDER BY u.first_name;
Is it the use of the word "DISTINCT?"
I appreciate the help, I have tried INNER and LEFT JOINS, but my mind is just blanking on this and I can't figure out how to get it to work.
Here is the query that IS WORKING and shows me more users than the above query:
SELECT f.source_id, u1.first_name AS s_firstname, u1.last_name AS s_lastname, f.target_id, u2.first_name AS t_firstname, u2.last_name AS t_lastname
FROM friendships AS f, users AS u1, users AS u2
WHERE f.mutual = 'true'
AND u1.id = f.source_id AND u2.id = f.target_id
AND f.created_at BETWEEN '2016-01-20' AND '2016-01-27';
Friendship Table Definition:
User Table Definition:
You must either
1. UNION (distinct) the projections of your second query on source and target or
2. select distinct users where they are a source or target
SELECT DISTINCT u.id, u.first_name, u.last_name
FROM friendships AS f, users AS u
WHERE f.mutual = 'true' -- both are friends
AND (u.id = f.source_id OR u.id = f.target_id)
AND f.created_at BETWEEN '2016-01-20' AND '2016-01-27'
ORDER BY u.first_name;
This will avoid any "duplicates" between u1 * u2. Either one (or more) friendship records exist (in the given period) or they don't.
SELECT u1.first_name AS s_firstname, u1.last_name AS s_lastname
, u2.first_name AS t_firstname, u2.last_name AS t_lastname
FROM users AS u1
JOIN users AS u2
ON EXISTS (
SELECT 13 FROM friendships f
WHERE u1.id = f.source_id AND u2.id = f.target_id
AND f.mutual = 'true'
AND f.created_at BETWEEN '2016-01-20' AND '2016-01-27';
);

How can I get records from one table which do not exist in a related table?

I have this users table:
and this relationships table:
So each user is paired with another one in the relationships table.
Now I want to get a list of users which are not in the relationships table, in either of the two columns (user_id or pair_id).
How could I write that query?
First try:
SELECT users.id
FROM users
LEFT OUTER JOIN relationships
ON users.id = relationships.user_id
WHERE relationships.user_id IS NULL;
Output:
This is should display only 2 results: 5 and 6. The result 8 is not correct, as it already exists in relationships. Of course I'm aware that the query is not correct, how can I fix it?
I'm using PostgreSQL.
You need to compare to both values in the on statement:
SELECT u.id
FROM users u LEFT OUTER JOIN
relationships r
ON u.id = r.user_id or u.id = r.pair_id
WHERE r.user_id IS NULL;
In general, or in an on clause can be inefficient. I would recommend replacing this with two not exists statements:
SELECT u.id
FROM users u
WHERE NOT EXISTS (SELECT 1 FROM relationships r WHERE u.id = r.user_id) AND
NOT EXISTS (SELECT 1 FROM relationships r WHERE u.id = r.pair_id);
I like the set operators
select id from users
except
select user_id from relationships
except
select pair_id from relationships
or
select id from users
except
(select user_id from relationships
union
select pair_id from relationships
)
This is a special case of:
Select rows which are not present in other table
I suppose this will be simplest and fastest:
SELECT u.id
FROM users u
WHERE NOT EXISTS (
SELECT 1
FROM relationships r
WHERE u.id IN (r.user_id, r.pair_id)
);
In Postgres, u.id IN (r.user_id, r.pair_id) is just short for:(u.id = r.user_id OR u.id = r.pair_id).
The expression is transformed that way internally, which can be observed from EXPLAIN ANALYZE.
To clear up speculations in the comments: Modern versions of Postgres are going to use matching indexes on user_id, and / or pair_id with this sort of query.
Something like:
select u.id
from users u
where u.id not in (select r.user_id from relationships r)
and u.id not in (select r.pair_id from relationships r)

select one of multiple rows generated by a join based on certain column

Alright say I have a tickets table, users table, roles table, and role_mapping table. A ticket is owned by a user. A user can have 1 or multiple roles. The roles table defines roles, while the role_mapping table maps a user to his/her roles (roles are basically permissions). So what I want to do is join users, tickets, and role_mapping as such (Oracle SQL):
SELECT t.ticket_id, u.user_id, rm.role_id
FROM tickets t
JOIN users u on u.user_id = t.user_id
JOIN role_mappings rm on rm.user_id = u.user_id
WHERE ...
So this of course generates multiple rows with the same ticket_id if a given user has multiple roles. What I want is if the role_id is say '102', only generate one row with value '102', otherwise, generate only one row corresponding to the maximum role value a given user has ie, if a user has role_id 101,102,105 then select 102, but if 101,105 then select 105.
How is this achievable?
I think you are making a mistake in your example (102 picked when you have 101,102,105 doesn't align with your written description) - but what you are looking for is group by after your joins and using an aggregate function (max) to pick your maximum value role. Something like:
SELECT t.ticket_id, u.user_id, max(rm.role_id)
FROM tickets t
JOIN users u on u.user_id = t.user_id
JOIN role_mappings rm on rm.user_id = u.user_id
WHERE ...
GROUP BY t.ticket_id, u.user_id
If you want all the roles in one line check out GROUP_CONCAT
UPDATE
To meet the requirement of role 102 taking precedence one way would be to exclude results which have ticket-users with role 102 and then adding them in in a second query using union
Something like this:
SELECT t.ticket_id, u.user_id, max(rm.role_id)
FROM tickets t
JOIN users u on u.user_id = t.user_id
JOIN role_mappings rm on rm.user_id = u.user_id
WHERE u.user_id NOT IN (
SELECT t.ticket_id, u.user_id, max(rm.role_id)
FROM tickets t
JOIN users u on u.user_id = t.user_id
JOIN role_mappings rm on rm.user_id = u.user_id
WHERE rm.role_id = 102)
GROUP BY u.id
UNION ALL
SELECT t.ticket_id, u.user_id, rm.role_id
FROM tickets t
JOIN users u on u.user_id = t.user_id
JOIN role_mappings rm on rm.user_id = u.user_id
WHERE rm.role_id=102
Fiddle here without the tickets table: http://sqlfiddle.com/#!2/cc1c6/5

How to do joins with conditions?

I always struggle with joins within Access. Can someone guide me?
4 tables.
Contest (id, user_id, pageviews)
Users (id, role_name, location)
Roles (id, role_name, type1, type2, type3)
Locations (id, location_name, city, state)
Regarding the Roles table -- type1, type2, type3 will have a Y if role_name is this type. So if "Regular" for role_name would have a Y within type1, "Moderator" for role-name would have a Y within type2, "Admin" for role_name would have a Y within type3. I didn't design this database.
So what I'm trying to do. I want to output the following: user_id, pageviews, role_name, city, state.
I'm selecting the user_id and pageviews from Contest. I then need to get the role_name of this user, so I need to join the Users table to the Contest table, right?
From there, I need to also select the location information from the Locations table -- I assume I just join on Locations.location_name = Users.location?
Here is the tricky part. I only want to output if type1, within the Roles table, is Y.
I'm lost!
As far as I can see, this is a query that can be built in the query design window, because you do not seem to need left joins or any other modifications, so:
SELECT Contest.user_id,
Contest.pageviews,
Roles.role_name,
Locations.city,
Locations.state
FROM ((Contest
INNER JOIN Users
ON Contest.user_id = Users.id)
INNER JOIN Roles
ON Users.role_name = Roles.role_name)
INNER JOIN Locations
ON Users.location = Locations.location_name
WHERE Roles.type1="Y"
Lots of parentheses :)
select *
from users u
inner join contest c on u.id = c.user_id and
inner join locations l on l.id = u.location and
inner join roles r on r.role_name = u.role_name
where r.type1 = 'Y'
This is assuming that location in users refers to the location id, if it is location name then it has to be joined to that column in locations table.
EDIT: The answer accepted is better, I did not consider that access needs parentheses.
Can you show what query you are currently using? Can't you just join on role_name and just ignore the type1, type2, type3? I am assuming there are just those 3 role_names available.
I know you didn't design it, but can you change the structure? Sometimes it's better to move to a sturdy foundation rather than living in the house that is about to fall on your head.
SELECT u.user_id, c.pageviews,
IIF(r.role_Name = "Moderator", r.type1 = Y,
IIF(r.role_name="Admin", r.type2="Y", r.type3="Y")),
l.location_name FROM users as u
INNER JOIN roles as r On (u.role_name = r.role_name)
INNER JOIN contest as c On (c.user_id = u.Id)
INNER JOIN locations as l On (u.location = l.location_name or l.id)
depending on whether the location in your user table is an id or the actual name reference.
I think I need to see some sample data....I do not understand the relationship between Users and Roles because there is a field role_name within the Users table, and how does that relate the the Roles Table?
EDIT NOTE Now using SQL Explicit Join Best Practice
SELECT
C.user_id
, C.pageviews
, U.role_name
, L.city
, L.state
FROM
Contest C
INNER JOIN Users U ON C.user_id = U.id
INNER JOIN Locations L ON U.location = L.id
INNER JOIN Roles R ON U.role_name = R.role_name
WHERE
R.type1='Y'