Reputation: 995
SQL SERVER 2008 R2. Two tables having a 1-many relationship, and want some fields from both, but on the many side, only values from a certain row. That row is specified by some condition, in my case it should the row having the smallest ClaimId for that PaymentId.
IF OBJECT_ID('tempdb..#Payments') IS NOT NULL DROP TABLE #Payments
CREATE TABLE #Payments ( PaymentId int UNIQUE NOT NULL )
INSERT INTO #Payments ( PaymentId )
VALUES (1), (2), (3)
IF OBJECT_ID('tempdb..#Claims') IS NOT NULL DROP TABLE #Claims
CREATE TABLE #Claims ( PaymentId int NOT NULL, ClaimId int NOT NULL, FirstName varchar(50) NOT NULL )
INSERT INTO #Claims ( PaymentId, ClaimId, FirstName )
VALUES (1, 51,'Joe'), (1, 57,'Jane'), (2, 62,'Spot'), (2, 63,'Rover'), (3, 88,'Sue'), (3, 89,'Sally')
SELECT * FROM #Payments p
JOIN #Claims c ON p.PaymentId=c.PaymentId
WHERE c.ClaimId=MIN(ClaimId)
GROUP BY p.PaymentId
HAVING MIN(ClaimId)
The desired result is as follows, where the claim is from the row having the minimum claimId for a given paymentId. ClaimId may not be ordered, so seeking minimum, not first.
PaymentId ClaimId FirstName
1 51 Joe
2 62 Spot
3 88 Sue
I'd be happy to refer to an existing question/answer, but don't know how to word this that I find something similar. Perhaps Get field value from a record that causes an aggregate condition to be true but I didn't understand it.
Upvotes: 1
Views: 81
Reputation: 10807
Both sentences return same result, first one select MIN(ClaimId) for every PaymentId:
SELECT P.*, C.*
FROM #Payments P
INNER JOIN #Claims C ON C.PaymentId = P.PaymentId
WHERE C.ClaimID IN (SELECT MIN(ClaimID) OVER (PARTITION BY PaymentId) FROM #Claims);
Second uses a CTE to find MIN(ClaimId) before to join to Payments table:cº
WITH PY AS
(
SELECT PaymentId, MIN(ClaimId) as ClaimID
FROM #Claims
GROUP BY PaymentId
)
SELECT PY.PayMentId, PY.ClaimId, C.FirstName, P.*
FROM PY
LEFT JOIN #Claims c ON p.ClaimId = c.ClaimId;
LEFT JOIN #Payments P ON P.PaymentId = PY.PaymentId
+-----------+---------+-----------+
| PayMentId | ClaimId | FirstName |
+-----------+---------+-----------+
| 1 | 51 | Joe |
+-----------+---------+-----------+
| 2 | 62 | Spot |
+-----------+---------+-----------+
| 3 | 88 | Sue |
+-----------+---------+-----------+
Check it here: http://rextester.com/KRT45653
Upvotes: 1
Reputation: 5594
use windows function row_number to provide an order
;with cte as
(
select PaymentID, ClaimID, row_number() over (partition by PaymentID order by ClaimID ) as rn
from #Claims
)
select p.*,cte.ClaimID
from #Payments p
join cte on cte.paymentID = p.paymentID
where rn=1 --limits to earliest claimid
Upvotes: 1
Reputation: 3810
solution using RANK()
;WITH A
AS (SELECT p.PaymentId
, c.ClaimId
, c.FirstName
, rn = RANK() OVER(PARTITION BY P.PaymentId ORDER BY ClaimId ASC)
FROM #Payments p
JOIN #Claims c ON p.PaymentId = c.PaymentId)
SELECT PaymentId
, ClaimId
, FirstName
FROM A
WHERE rn = 1;
Upvotes: 1
Reputation: 1270021
In SQL Server, I like to use outer apply
for this:
select p.*, c.*
from #Payments p outer apply
(select top 1 c.*
from #Claims c
where c.paymentid = p.paymentid
order by c.claimid
) c;
Upvotes: 1
Reputation: 26674
You can use the ROW_NUMBER
window function like this:
SELECT PaymentId, ClaimId, FirstName
FROM
(
SELECT
p.PaymentId,
c.ClaimId,
c.FirstName,
ROW_NUMBER() OVER (PARTITION BY p.PaymentId ORDER BY c.ClaimId) as RN
FROM #Payments p
JOIN #Claims c ON p.PaymentId=c.PaymentId
) as T
WHERE RN = 1
Upvotes: 2