Tricky query in DB2 - sql

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

Related

string_agg () is not working for Postgres sql when joining on multiple tables

I have to join user_name from user table where as first_name and last_names from user_profile table. Until here everything is fine, but when I try to fetch respective roles assigned from user_role tables it gives multiple rows for single user as 1 user can have multiple roles.
While trying to apply string_agg on role.names (so that multiple roles shown comma separated in single tuple), it gives each role in separate row.
Here is example query I am trying to run in postgresql:
SELECT users.user_name, user_profiles.first_name, user_profiles.last_name,
(
SELECT string_agg (roles.name, ',')
from roles
where roles.id in (
select user_roles.role_id where users.id = user_roles.user_id
)
) as name
FROM users
JOIN user_profiles ON users.id = user_profiles.user_id
JOIN user_roles ON user_roles.user_id = users.id
You must use GROUP BY clause in order to aggregate more than one record together within a group. That along with the unnecessary (I believe) nested SQL is leading you to wrong results.
Instead consider the following:
SELECT users.user_name, user_profiles.first_name, user_profiles.last_name,
string_agg (roles.name, ',') as name
FROM users
JOIN user_profiles
ON users.id = user_profiles.user_id
JOIN user_roles
ON user_roles.user_id = users.id
JOIN roles ON user_roles.role_id = roles.id
GROUP BY users.user_name, user_profiles.first_name, user_profiles.last_name
Online demo
You forgot a from in your innermost subquery:
SELECT users.user_name,
user_profiles.first_name,
user_profiles.last_name,
(SELECT string_agg (roles.name, ', ')
from roles
where roles.id
in ( select u.role_id
from user_roles u --this was missing
where users.id = u.user_id)) as name
FROM users
JOIN user_profiles
ON users.id = user_profiles.user_id
-- JOIN user_roles --you're already getting roles in the innermost query
-- ON user_roles.user_id = users.id;
without it, that subquery was just selecting one, current row's role_id, instead independently fetching of all roles for the user as you could've expected it to. See this demo.
Note that once this is fixed, you also need to get rid of the last join (commented out in my example) to avoid multiplying users for each of their roles - you're already getting their roles in the innermost subquery anyways.
It would be easier to use a regular group by:
select users.user_name,
user_profiles.first_name,
user_profiles.last_name,
string_agg(roles.name,', ')
from users
inner join user_profiles
on users.id=user_profiles.user_id
inner join user_roles
on user_roles.user_id=users.id
inner join roles
on roles.id=user_roles.role_id
group by 1,2,3;

Performance of JOIN operation

Lets suppose I have two huge tables
users(user_id, name, country)
location(id, user_id, country, city)
If i want to fetch for particular country
1.
select * from users u
inner join location l on u.user_id = l.user_id
where l.country = 'heaven'
2.
select * from users u
inner join location l on u.user_id = l.user_id
where u.country = 'heaven'
3.
select * from users u
inner join location l on u.user_id = l.user_id
where u.country = 'heaven' and l.country = 'heaven'
Which of the three would be better approach?
and suppose the result of filtering data by country would be
users table has 1000 record with country='heaven'
location table has 2 record with country='heaven'
What will be performance now?
Your queries would only return the same results, if one of the following is true:
location records did not exist for all users.
users has duplicate user_ids.
It is unclear which. And, having country in both tables suggests that something is wrong with your data model.
That said, if you need both filtering by the join and filtering on the country, I would recommend:
select *
from users u join
locations l
on u.user_id = l.user_id
where l.country = 'heaven';
And you want an index on locations(country, user_id). This should find the two records in locations and look up the corresponding values in users. That seems like the fastest way to do what you want.

How can I return rows from an SQL query where one unique ID satisfies conditions in different rows?

I have a table users which has a foreign key in my other table, usersHaveSocialMediaFans.
usersHaveSocialMediaFans has columns:
socialMediaId, userId, fanCount
I need to find all users that have a fan count greater than (or less than) a certain amount for each social media. Admins need to be able to filter out all users that might have at least 6 Facebook fans AND 7 Twitter followers.
It was super easy when I had this information in my user table:
userId, ..., facebookFans, twitterFollowers, ...
---0--, ---, -----10-----, ------1000------, ...
Then, I could just SELECT userId from users WHERE facebookFans > # AND twitterFollowers > #; however, now my data for each user is split up into multiple rows and I don't know how to handle the logic. I feel like there must be a simple answer, but I just can't think of it right now.
One way to do it
SELECT U.userId, F.SocialMediaID, F.fanCount from users U
LEFT JOIN usersHaveSocialMediaFans F
ON F.Userid = U.Userid
WHERE (F.SocialMediaID='Facebook' and Fancount>1000)
And (F.SocialMediaID='Twitter' and Fancount>1000)
another way to do it:
SELECT U.userId, F.fanCount "FacebookFan", t.fanCount "TwitterFan" from users U
LEFT JOIN usersHaveSocialMediaFans F
ON F.Userid = U.Userid and F.SocialMediaID= 'Facebook'
LEFT JOIN usersHaveSocialMediaFans T
ON T.Userid = U.Userid and T.SocialMediaID= 'Twitter'
WHERE F.FanCount > 1000 and T.FanCount > 1000
You just need to JOIN to your usersHaveSocialMediaFans table twice for it. For example, if you wanted those with more than 1,000 Facebook fans, and more than 10 Twitter fans, you could do the following:
Select Distinct U.UserId
From Users U
Join usersHaveSocialMediaFans F On F.UserId = U.UserId
And F.socialMediaId = <FacebookSocialMediaId>
Join usersHaveSocialMediaFans T On T.UserId = U.UserId
And T.socialMediaId = <TwitterSocialMediaId>
Where F.fanCount > 1000
And T.fanCount > 10;
If you have another table that stores the name of the SocialMediaId for each platform, you should do another JOIN there instead of hard-coding in the SocialMediaId's for each platform. But I don't know your schema, so hopefully this will point you in the right direction.
If, hypothetically, you had another table named SocialMedia that had the SocialMediaId and Name of each platform, you could do the following:
Select Distinct U.UserId
From Users U
Join usersHaveSocialMediaFans F On F.UserId = U.UserId
Join SocialMedia SF On SF.SocialMediaId = F.SocialMediaId
And SF.Name = 'Facebook'
Join usersHaveSocialMediaFans T On T.UserId = U.UserId
Join SocialMedia ST On ST.SocialMediaId = T.SocialMediaId
And ST.Name = 'Twitter'
Where F.fanCount > 1000
And T.fanCount > 10;
Hopefully this helps!

Select without Group By Sql

I got some problems with this query i need all my selectors but i only want to group aspnet_Users.Userid how should i do this?
Here is my query:
SELECT
aspnet_Users.UserId, aspnet_Users.UserName,
Friends.Verified, Friends.FriendUserId
FROM
[aspnet_Users]
INNER JOIN
Friends ON Friends.UserId = aspnet_Users.UserId OR Friends.FriendUserId = aspnet_Users.UserId
WHERE
aspnet_Users.UserId IN
(SELECT UserId as Id FROM Friends
WHERE FriendUserId='3d1224ac-f2ad-45d4-aa84-a98e748e3e57'
UNION
SELECT FriendUserId as Id FROM Friends
WHERE UserId='3d1224ac-f2ad-45d4-aa84-a98e748e3e57')
GROUP BY
aspnet_Users.UserId, aspnet_Users.UserName,
Friends.Verified, Friends.FriendUserId
Stored Procedure structure:
Tables [aspnet_Users] and [Friends]
Columns [aspnet_Users.UserId], [aspnet_Users.UserName], [Friends.Verified], [Friends.FriendUserId]
That is the data that i need to get using the procedure, but the problem is that each user can have multiply friends which means if so there will be multiply Friends values. This is the reason why i can't group using those values cause it gives me dublicates with the wrong value.
Expected Output:
aspnet_Users.UserId (This is the user id of the friend, note that it doesn't mean that its the FriendUserId)
aspnet_Users.UserName (This is the UserName of the friend based on the UserId explained above)
Friends.Verified (True or False. The Verified value in the Friends table on the friendship)
Friends.FriendUserId (The FriendUserId value from the table Friends)
If your relationship is not fully reciprocal in the table Friends, you can use this query.
Otherwise either part of the UNION would work on its own
SELECT U.UserId, U.UserName, F.Verified, F.FriendUserId
FROM Friends F
JOIN aspnet_Users U ON U.UserId=F.UserId
WHERE F.FriendUserId='3d1224ac-f2ad-45d4-aa84-a98e748e3e57'
UNION
SELECT U.UserId, U.UserName, F.Verified, F.FriendUserId
FROM Friends F
JOIN aspnet_Users U ON U.UserId=F.FriendUserId
WHERE F.UserId='3d1224ac-f2ad-45d4-aa84-a98e748e3e57'
Do you need to group? You aren't using any aggregate functions (eg sum, count)
You haven't made it clear what results you're looking for - I'm guessing all details of users connected with a given user, including the user themselves?
Surely you can just do something like:
Select a.userid, a.username, f.verified, f.userid
From aspnet_users a
Full outer join friends f on f.frienduserid = a.userid
Where a.userid = #userid or f.frienduserid = #userid
You can use DISTINCT
SELECT DISTINCT
aspnet_Users.UserId, aspnet_Users.UserName,
Friends.Verified, Friends.FriendUserId
FROM
[aspnet_Users]
INNER JOIN
Friends ON Friends.UserId = aspnet_Users.UserId OR Friends.FriendUserId = aspnet_Users.UserId
WHERE
aspnet_Users.UserId IN
(SELECT UserId as Id FROM Friends
WHERE FriendUserId='3d1224ac-f2ad-45d4-aa84-a98e748e3e57'
UNION
SELECT FriendUserId as Id FROM Friends
WHERE UserId='3d1224ac-f2ad-45d4-aa84-a98e748e3e57')

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'