How to Join only first row, disregard further matches - sql

I have 2 tables
Table Users:
UserID | Name
Table Cars:
CarID | Car Name | FK_UserID
A user can have more than 1 car.
I want to join each user with 1 car only, not more.
Having looked at other threads here,
I've tried the following:
Select users.UserID, users.name, carid
from Users
join cars
on users.UserID =
(
select top 1 UserID
from users
where UserID = CarID
)
But it still returns more than 1 match for each user.
What am I doing wrong?

You can try like below using ROW_NUMBER() function
select userid, username, carname
from
(
Select users.UserID as userid,
users.name as username,
cars.carname as carname,
ROW_NUMBER() OVER(PARTITION BY users.UserID ORDER BY users.UserID) AS r
from Users
join cars
on users.UserID = cars.FK_UserID
) XXX
where r = 1;

with x as
(select row_number() over(partition by userid order by carid) as rn,
* from cars)
select u.userid, x.carid, x.carname
from users u join x on x.userid = u.userid
where x.rn = 1;
This is one way to do it using row_number function.

Another way to do it
select u.UserID,
u.name,
(select TOP 1 carid
from cars c
where u.UserID = c.FK_UserID
order by carid) carid -- Could be ordered by anything
from Users u
-- where only required if you only want users with cars
where exists (select * from car c where u.UserID = c.FK_UserID)

Best would be to do a subquery and use a group-by in it to return only 1 user and a car for each user. Then join that to the outer user table.
Here is an example:
select *
from user_table u
join (
select userid
, max(carname)
from cars
group by userid
) x on x.userId = u.userId
or you could use the row_number() examples above if you want a specific order (either this example or theirs will do the trick)

Related

SQL conditional field, first match JOIN

Lets imagine I have two tables:
user
--userid
--fname
--lname
widget
--id
--userid
--value
user.userid = widget.userid
I want to see the full list of users with the Widget.value if they have one, AND(!) the first match if there are more than 1 widget. No widget = null field
id fname lname value
1 John Doe X8
I can not do simple joins, cos if there is no 'widget.value' for some 'user' user won't be displayed
CROSS APPLY doesn't work as well
I need
1 widget = value
2 widgets = first one
0 widgets = null field
using top with ties:
select top 1 with ties
u.*, w.id, w.value
from dbo.user u
left join dbo.widget w
on u.userid = w.userid
order by row_number() over (partition by u.userid order by w.id);
using common table expression with row_number()
;with cte as (
select u.*, w.id, w.value
, rn = row_number() over (partition by u.userid order by w.id)
from dbo.user u
left join dbo.widget w
on u.userid = w.userid
)
select *
from cte
where rn = 1;
outer apply should do what you want:
select u.*, w.value
from user u outer apply
(select top 1 w.*
from widgets w
where w.userid = u.userid
order by id -- or however you define the first one
) w;
Try this:
SELECT
u.userid, u.fname, u.lname, w.value
FROM user as u
LEFT JOIN
(
SELECT w1.*
FROM widget as w1
INNER JOIN
(
SELECT userid, MAX(id) AS LatestId
FROM widget
GROUP BY userid
) AS w2 ON w1.userid = w2.userid and w1.id = w2.latestid
) AS w ON u.userid = w.userid;
The inner join with subquery with max and group by, will give you the latest row for each userid if any. So for those with more than 1 row you will get the latest one.
There is no date, so I assumed the max id is the latest one, which might not always the case.
LEFT JOIN will include those rows with un matched rows form the widget table, so if there is a user with no widget you will get a value null.
SELECT u.userid, w.value
FROM user u
OUTER APPLY (
SELECT TOP 1 w.value
FROM widget w
WHERE w.userid = u.userid
ORDER BY w.id --order by whatever makes a widget the first one
) w

t-sql query with deep joining

I'm having a little trouble of getting a fast sql-query on this. I've managed to get a query to return the result I want but it takes about 2 sek to run even with the right indexes.
I have these tables:
[Login]
loginID
loginTime
userID
[user]
userID
userName
[companyParticipant]
userID
companyID
[company]
companyID
organisationID
CompanyName
What I want to show is all the top 10 latest logins persons with loginTime. Where the user is in a company that I am a participant or a company within the organisations where I am member of a company of that organisation
To get my organisations:
SELECT organisationID
FROM companys
WHERE companyID IN (
SELECT companyID
FROM companyParticipant
WHERE userID = #userID)
GROUP BY organisationID
So what i want i a query like this:
SELECT TOP 10 userName, LoginTime
FROM ....
ORDER BY loginID
SELECT userName, loginTime
FROM
(
SELECT u.userName, l.loginTime,
rn = row_number() over (partition BY u.userName
ORDER BY l.loginTime DESC)
FROM companyParticipant cp
JOIN companys c ON c.companyID = cp.companyID
JOIN companys c2 ON c2.organisationID = c.organisationID -- same organisation
JOIN companyParticipant cp2 ON cp2.companyID = c2.companyID -- participants of same org
JOIN login l ON l.userID = cp2.userID
JOIN [user] u ON u.userID = l.userID
WHERE cp.userID = #userID
) X
WHERE rn = 1
ORDER BY loginTime DESC
This query goes under a second and is superfast. Seems strange
no need for tiebreaking either beacuase there is always people logging in and a new login must be 10 minutes apart otherwise it just updates the last login.
SELECT TOP (10) l.loginID, l.loginTime,u.userName
FROM logins AS l WITH(NOLOCK) INNER JOIN
users AS u WITH(NOLOCK) ON l.UserID = u.UserID
WHERE (l.UserID <> #userID)
AND u.userID IN(SELECT u.userID FROM companyParticipants AS sp2 WHERE sp2.companyID IN (SELECT sc2.companyID FROM company AS sc2 WHERE sc2.organisationID IN(
SELECT sc.organisationID FROM company AS sc LEFT JOIN companyParticipants AS sp ON sc.companyID = sp.companyID WHERE sp.userID = #userID AND sc.organisationID > 0 GROUP BY sc.organisationID
)))
ORDER BY l.loginID DESC

How can I take the first match from table B to table A when there's multiple matches?

If I have a User table, like this:
UserId Name
------ ----
1 Jim
2 Mark
and an Order table like this:
OrderId UserId
------- ------
1 1
2 1
3 1
4 2
How can I just take any single Order of Jim's? It doesn't matter which, I just want one.
I have this:
Joining isn't the issue, it's just limiting it to one result as there are obviously 3.
SELECT * FROM User u
LEFT OUTER JOIN Order o on u.UserId = o.UserId
Thanks in advance.
This will select the first matched row:
SELECT TOP 1 * FROM User u
LEFT OUTER JOIN Order o ON u.UserID = o.UserID
If you know which user you're looking for ahead of time, you can add that to the WHERE clause:
WHERE u.UserID = #userID
And, if you ever need to make sure you're getting the First, or Most Recent order, you can use :
ORDER BY o.OrderID ASC/DESC
Select u.userid, name, min (orderID)
FROM User u
LEFT JOIN Order o on u.UserId = o.UserId
group by u.userid, name
To filter just for Jim
Select min (orderID)
FROM User u
LEFT JOIN Order o on u.UserId = o.UserId
where name='Jim'
the following query will give you one order per user (the lowest order number of each user)
select name, order_id
from users u,
orders o
where u.user_id = o.user_id
and o.order_id = (select min(order_id) from orders c where c.user_id=o.user_id)
select u.userid,
u.name,
o.orderid
from users u
join (select orderid,
userid,
row_number() over (partition by userid order by orderid) as rn
from orders) o
on o.userid = u.userid and o.rn = 1;
You can control which orders is being taken by tweaking the order by part of the window definition.

SQL: Finding user with most number of comments

I need to find out the user who has posted the most number of comments. There are two tables 1)users(Id, DisplayName) 2)comments(Id, UserId, test) . I have used the following query
Select DisplayName from users INNER JOIN (Select UserId, max(comment_count) as `max_comments from (Select UserId, count(Id) as comment_count from comments group by UserId) as` T1) as T2 ON users.Id=T2.UserId
However, this returns to me the Display Name of the user with Id = 1 rather than what I want. How do I work around this ?
SELECT TOP 1
U.DisplayName,
COUNT(C.ID) AS CommentCount
FROM
Users AS U
INNER JOIN Comments AS C ON U.ID = C.UserID
GROUP BY
U.DisplayName
ORDER BY
COUNT(C.ID) DESC

How to select all users who made more than 10 submissions

I have a submission table that is very simple: userId, submissionGuid
I want to select the username (simple inner join to get it) of all the users who have more than 10 submissions in the table.
I would do this with embedded queries and a group by to count submissions... but is there a better way of doing it (without embedded queries)?
Thanks!
This is the simplest way, I believe:
select userId
from submission
group by userId
having count(submissionGuid) > 10
select userId, count(*)
from submissions
having count(*) > 10
group by userId
SELECT
username
FROM
usertable
JOIN submissions
ON usertable.userid = submissions.userid
GROUP BY
usertable.username
HAVING
Count(*) > 1
*Assuming that your "Users" table is call usertable and that it has a column called "UserName"
I think the correct query is this (SQL Server):
SELECT s.userId, u.userName
FROM submission s INNER JOIN users u on u.userId = s.userId
GROUP BY s.userId, u.username
HAVING COUNT(submissionGuid) > 10
If you don't have the HAVING clause:
SELECT u.userId, u.userName
FROM users u INNER JOIN (
SELECT userId, COUNT(submissionGuid) AS cnt
FROM submission
GROUP BY userId ) sc ON sc.userId = u.userId
WHERE sc.cnt > 10
select userid, count(submissionGUID) as submitCount
from Submissions
group by userid, submitCount
having submitCount > 10