querying data between three tables - sql

Just wanted confirmation to make sure I'm doing this right before I try it. Kinda weak on joins.
I have three tables:
USER PERSONGROUPTOPERRSON PERSONGROUPS
id userId groupID
firstName groupId groupName
lastName
lastUpdated
I need all users who belong to groups with "volunteer" in the group name.
Would this query be correct?
SELECT
a.firstName, a.lastName, a.lastUpdated
FROM
user as a, persongrouptoperson as b
INNER JOIN ON
a.id = b.userId
WHERE
b.groupId
IN
(SELECT
groupID
FROM
persongroups
WHERE
groupName like '%volunteer%'
);

SELECT
a.firstName, a.lastName, a.lastUpdated
FROM
user as a
INNER JOIN persongrouptoperson as b ON
a.id = b.userId
INNER JOIN persongroups as c ON
b.groupID = c.groupID
WHERE c.groupName like '%volunteer%'

You can do this with inner joins. I added distinct in case your users can be in multiple groups with the word volunteer in it. I also recommend using more intuitive aliases for tables instead of a, b, c.
select distinct
u.firstName
, u.lastName
, u.lastUpdated
from User as u
inner join PersonGroupToPerson as pgp on u.id = pgp.userId
inner join PersonGroup as pg on pgp.groupId = pg.groupId
where pg.GroupName like '%volunteer%'

Not exactly. Try it like this:
SELECT user.firstname, user.lastname, user.lastupdated
FROM user
INNER JOIN persongrouptoperson ON user.id = persongrouptoperson.userid
INNER JOIN persongroups ON persongrouptoperson.groupid = persongroups.groupid
WHERE groupName like '%volunteer%'
The idea is to join all three tables together. You can do this pretty simply with inner joins. You just have to link the common keys. So for instance, the user id field in the user table matches the userid field in the person group to person table. Then the group id field in the person groups table matches the group id field in the person group to person table. Link them with ON statements.
Once the tables are linked, you can imagine that each row contains the user information and the group information. So your where clause can directly call out what you are looking for in that group information.

Related

Join on two of the same foreign keys

I have this table below
Table Assignments
AssignmentID: int
LinkedTo: varchar(50)
AssignedUser: int
AssignedBy: int
where AssignedBy and AssignedUser are foreign keys from the table Users. Here is what Users looks like
UserKey:int
Username:varchar(50)
How can I do an inner join where I get both AssignedBy and AssignedUser?
The following gives me one of them but how can I get both? By the way AssignedBy and AssignedUser are two different Users. I'm trying to get Users.Username
select Users.Username
from Assignments
INNER JOIN Users ON Assignments.UserKey = Users.UserKey
If you want both user names on one row, you have to join Users twice, like
SELECT UA.Username AS AssignedUserName, UB.Username AS AssignedByUserName
FROM Assignments A
INNER JOIN Users UA ON A.AssignedUser = UA.UserKey
INNER JOIN Users UB ON A.AssignedBy = UB.UserKey;
If your goal is to have all user names regardless wether it comes from AssignedUser or from AssignedBy, you have to use UNION
SELECT U.Username
FROM Assignments A
INNER JOIN Users U ON A.AssignedUser = U.UserKey
UNION SELECT U.Username
FROM Assignments A
INNER JOIN Users U ON A.AssignedBy = U.UserKey;
Note however that this will remove duplicate user names. If you want to keep duplicates, use UNION ALL.
You need two joins:
SELECT assigned.username AS assigned_username
assigned_by.username AS assigned_by_username
FROM assignments a
JOIN users assigned ON a.assigneduser = assigned.userkey
JOIN users assigned_by ON a.assignedby = assigned_by.userkey

Sql match id's with names from a table that matches with another table

I have a table friend which has two columns with Id's that belong to specific usernames. See example:
Id_Gebruiker is the user and Id_Gebruiker2 is the friend of the user.
The table friend has id's that belong to the table user.
Then that table user has Foreign Keys (column Id_Client) to the table client (referencing the Id_Client column). The client table contains the usernames.
Now I want to get the corresponding usernames that belongs to the Id's that the Friend table contains. So far I got it working when I use only two tables but I can't get it to work with three tables.
Something like this?
select f.Id_Gebruiker, f.Id_Gebruiker2, u.Id_Client, c.Gebruikersnaam
from friend f
join [user] u on f.Id_Gebruiker2 = u.Id
join client c on u.id_client = c.id
EDIT Assuming the names of the users are on the same table (client) and that every friend.Id_Gebruiker corresponds to a client.Id (so there exists a FK relationship), you can find both names with an extra join:
select c.Gebruikersnaam as UserName, c2.Gebruikersnaam as FriendName
from friend f
join client c on f.Id_Gebruiker = c.Id
join [user] u on f.Id_Gebruiker2 = u.Id
join client c2 on u.id_client = c2.id
Here is another answer that you can check and verify at http://sqlfiddle.com/#!3/2b9dd/1.
The main query for getting names for userid and friendid in friends table is as below.
SELECT f1.userid,
f1.friendid,
y.username AS UserName,
x.username AS FriendName
FROM friends f1
INNER JOIN (SELECT DISTINCT f.friendid,
c.clientname AS UserName
FROM users u
INNER JOIN clients c
ON u.id_client = c.id
INNER JOIN friends f
ON f.friendid = u.id) x
ON f1.friendid = x.friendid
INNER JOIN (SELECT DISTINCT f.userid,
c.clientname AS UserName
FROM users u
INNER JOIN clients c
ON u.id_client = c.id
INNER JOIN friends f
ON f.userid = u.id) y
ON f1.userid = y.userid;

How would I do this conditional join in MS SQL?

I searched around for conditional joins, but it seems like they're trying to do something different that what I'm trying. I'll simplify the problem a bit for the question.
I have three tables: Users, Clients, and Employees.
The Users table has two columns, AssociatedID and UserType.
UserType is either "Client" or "Employee," and AssociatedID is a corresponding ID in either the Clients or the Employees table.
The problem is, Clients and Employees both use integers for IDs, so each table could have the same ID for something completely different (as opposed to GUIDs, which would avoid this problem).
What I want is a query that will look at a row in Users and say if UserType is Employee, then JOIN AssociatedID on the Employees table's ID, and if UserType is Client, then JOIN AssocaitedID on the Clients table's ID. So if Clients and Employees both have a Name column, I could get all of the names that go with each User, regardless of if they're a Client or an Employee. I'm using SQL Server 2008 R2 if that makes a difference here.
Try this:
SELECT
COALESCE(c.Name, e.Name) AssociatedName,
...
FROM Users u
LEFT JOIN Clients c ON u.AssociatedID = c.ClientId
AND u.UserType = 'Client'
LEFT JOIN Employees e ON u.AssociatedID = e.EmployeeId
AND u.UserType = 'Employee';
SQL Fiddle demo
Or: As #Tikkes pointed out, you can do this with UNION like this:
SELECT c.Name AssociatedName
FROM Users u
INNER JOIN Clients c ON u.AssociatedID = c.ClientId
AND u.UserType = 'Client'
UNION ALL
SELECT e.Name FROM Users u
INNER JOIN Employees e ON u.AssociatedID = e.EmployeeId
AND u.UserType = 'Employee';
Updated SQL Fiddle Demo
Update: For this you have to use the CASE expression like this:
SELECT
u.AssociatedID,
CASE u.UserType
WHEN 'Client' THEN c.Name
ELSE e.Name
END AS AssociationName
FROM Users u
LEFT JOIN Clients c ON u.AssociatedID = c.ClientId
LEFT JOIN Employees e ON u.AssociatedID = e.EmployeeId;
Updated SQL Fiddle Demo using CASE
Same Senario: I have CustomerExt have SoldTo and ShipTo Code,
and if ShipTo column has value then use ShipToName in table ShipTo otherwise use CustomerName in Customer table
Select COALESCE(B.CustomerName, C.ShiptoName) as CustomerName
FROM CustomerExt A
left join Customer B ON A.SoldTo = B.CustomerCode
AND isnull(A.ShipTo,'') =''
left join ShipTo C ON A.ShipTo = C.ShipToCode
AND isnull(A.ShipTo,'') <>''

comparison query taking ages

My query is quite simple:
select a.ID, a.adres, a.place, a.postalcode
from COMPANIES a, COMPANIES b
where a.Postcode = b.Postcode
and a.Adres = b.Adres
and (
select COUNT(COMPANYID)
from USERS
where COMPANYID=a.ID
)>(
select COUNT(COMPANYID)
from USERS
where COMPANYID=b.ID
)
Database: sql server 2008 r2
What I'm trying to do:
The table of COMPANIES contains double entries. I want to know the ones that are connected to the most amount of users. So I only have to change the foreign keys of those with the least. ( I already know the id's of the doubles)
Right now it's taking a lot of time to complete. I was wondering if if could be done faster
Try this version. It should be only a little faster. The COUNT is quite slow. I've added a.ID <> b.ID to avoid few cases earlier.
select a.ID, a.adres, a.place, a.postalcode
from COMPANIES a INNER JOIN COMPANIES b
ON
a.ID <> b.ID
and a.Postcode = b.Postcode
and a.Adres = b.Adres
and (
select COUNT(COMPANYID)
from USERS
where COMPANYID=a.ID
)>(
select COUNT(COMPANYID)
from USERS
where COMPANYID=b.ID
)
The FROM ... INNER JOIN ... ON ... is a preferred SQL construct to join tables. It may be faster too.
One approach would be to pre-calculate the COMPANYID count before doing the join since you'll be repeatedly calculating it in the main query. i.e. something like:
insert into #CompanyCount (ID, IDCount)
select COMPANYID, COUNT(COMPANYID)
from USERS
group by COMPANYID
Then your main query:
select a.ID, a.adres, a.place, a.postalcode
from COMPANIES a
inner join #CompanyCount aCount on aCount.ID = a.ID
inner join COMPANIES b on b.Postcode = a.Postcode and b.Adres = a.Adres
inner join #CompanyCount bCount on bCount.ID = b.ID and aCount.IDCount > bCount.IDCount
If you want all instances of a even though there is no corresponding b then you'd need to have left outer joins to b and bCount.
However you need to look at the query plan - which indexes are you using - you probably want to have them on the IDs and the Postcode and Adres fields as a minimum since you're joining on them.
Build an index on postcode and adres
The database probably executes the subselects for every row. (Just guessing here, veryfy it in the explain plan. If this is the case you can rewrite the query to join with the inline views (note this is how it would look in oracle hop it works in sql server as well):
select distinct a.ID, a.adres, a.place, a.postalcode
from
COMPANIES a,
COMPANIES b,
(
select COUNT(COMPANYID) cnt, companyid
from USERS
group by companyid) cntA,
(
select COUNT(COMPANYID) cnt, companyid
from USERS
group by companyid) cntb
where a.Postcode = b.Postcode
and a.Adres = b.Adres
and a.ID<>b.ID
and cnta.cnt>cntb.cnt

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')