Reputation: 19
I'm trying to write a query based on accounts and their contracts. The table has all contracts for each account, whether the contract is active, expired, etc. I want the query to only bring back the contract with earliest start date per account, so only one row for each account. However i don't know the status of the earliest contract for each account. Some might have active, some might have pending. I run into the problem now where it brings back multiple records for each account if the contract status is in the list i specify. Simple sample code below:
Select t.account, t.contract, t.status Min(t.start_date)
From table t
where t.status in ('Active','Countersigned','Pending')
Upvotes: 0
Views: 2284
Reputation: 24541
If your database supports it (e.g. Oracle, Postgres, SQL Server, but not MySQL or SQLite), you can use Window Functions. For instance, you can rank your contracts within each account by starting_at:
SELECT *, rank() OVER (PARTITION BY account_id ORDER BY starting_at ASC) AS rank
FROM contracts
Then you can use that in a subquery to join to accounts and only take contracts with a rank of 1. You'll need to put it in a subquery, because unfortunately (in Postgres at least) you can't use window functions inside WHERE. So this won't work:
SELECT *, rank() OVER (PARTITION BY account_id ORDER BY starting_at ASC) AS rank
FROM contracts
WHERE rank = 1
but this will:
SELECT *
FROM (SELECT *, rank() OVER (PARTITION BY account_id ORDER BY starting_at ASC) AS rank
FROM contracts) x
WHERE rank = 1
Note you can easily add filtering by status, etc. to any of these queries.
Upvotes: 1
Reputation: 4262
a solution that works if you don't have multiple contracts for each account on the same MIN(date)
(in that case you'd get multiple rows for each account and you should decide which of these N contracts you want to see, I can't decide for you)
SELECT t.*
FROM (
Select t.account, Min(t.start_date) AS MinDate
From table t
where t.status in ('Active','Countersigned','Pending')
GROUP BY t.account
) AS t2
INNER JOIN table t ON t.account = t2.account AND t.start_date = t2.MinDate
Upvotes: 0
Reputation: 10875
This should work:
select account, contract, status, MinDate
from
(
Select t.account, t.contract, t.status, t.start_date,
Min(t.start_date) over(partition by t.account) MinDate
From table t
where t.status in ('Active','Countersigned','Pending')
) x
where start_date=MinDate
Upvotes: 0