Others answers are ok but I'm posting it for the cases there's hundres (or more) of categories because in this case populating a table variable is better than writing lots and lots of CASE
statements.
The trick here is to populate a table variable or temp table with your ordering rules and just aggregate by it.
create table dbo.[SAMPLE]
(
[STATUS] varchar(30) not null
,[INVOICE] int not null
)
GO
insert into dbo.[SAMPLE]
values
('processed', 100)
,('reconciled', 100)
,('reconciled', 200)
,('paid', 300)
,('paid', 100)
,('paid', 200)
GO
The below is a partial result showing how it get grouped by your ordering rules
--Processed > reconciled > paid
declare @Ordering as table
(
[STATUS] varchar(30) not null
,[Order] smallint not null
)
insert into @Ordering
values
('processed', 3)
,('reconciled',2)
,('paid',1)
select sp.[INVOICE], max(ord.[Order]) as Precedence
from dbo.[SAMPLE] sp
join @Ordering ord on ord.[STATUS] = sp.[STATUS]
group by sp.[INVOICE]
and below the final query with the expected results
--Processed > reconciled > paid
declare @Ordering as table
(
[STATUS] varchar(30) not null
,[Order] smallint not null
)
insert into @Ordering
values
('processed', 3)
,('reconciled',2)
,('paid',1)
select ord.[STATUS], grouped.INVOICE
from
(
select sp.[INVOICE], max(ord.[Order]) as Precedence
from dbo.[SAMPLE] sp
join @Ordering ord on ord.[STATUS] = sp.[STATUS]
group by sp.[INVOICE]
) as grouped
join @Ordering ord on ord.[Order] = grouped.Precedence
It can be also a interesting solution from performance perspective (acid test required of course).