Reputation: 1272
How can a select
be crafted to where the first and last row of each set of the rows are grouped with a GROUP BY
.
I've this table with the following data:
id group val start end
1 10 36 465 89
2 10 35 55 11
3 10 34 20 456
4 20 38 1140 1177
5 20 22 566 788
6 20 1235 789 4796
7 20 7894 741 1067
What I need to get is the first value of the column start and last value of the column end with group by the group column.
The resultant table should be as below:
id group val start end
1 10 36 465 89
3 10 34 20 456
4 20 38 1140 1177
7 20 7894 741 1067
I did a query but with FIRST_VALUE
and LAST_VALUE
and over (partition by)
. It works in SQL Server 2012 but didn't work in SQL Server 2008. I need a query that can be executed in SQL Server 2008.
Upvotes: 14
Views: 16785
Reputation: 71
Here is a bankaccount example:
create table transactions (
id integer identity(1,1),
aDateTime datetime not null default getdate(),
OnHand float,
AmountWithDrawn float,
AmountDeposited float,
NewOnhand float
)
insert into transactions (aDateTime, OnHand, AmountWithDrawn, AmountDeposited, NewOnhand) values ('01-jan-2021 08:15', 0, 0, 1000, 1000);
insert into transactions (aDateTime, OnHand, AmountWithDrawn, AmountDeposited, NewOnhand) values ('05-jan-2021 08:15', 1000, 100, 0, 900);
insert into transactions (aDateTime, OnHand, AmountWithDrawn, AmountDeposited, NewOnhand) values ('06-jan-2021 08:15', 900, 200, 0, 700);
insert into transactions (aDateTime, OnHand, AmountWithDrawn, AmountDeposited, NewOnhand) values ('01-feb-2021 08:15', 700, 0, 1000, 1700);
insert into transactions (aDateTime, OnHand, AmountWithDrawn, AmountDeposited, NewOnhand) values ('09-feb-2021 08:15', 1700, 200, 0, 1500);
insert into transactions (aDateTime, OnHand, AmountWithDrawn, AmountDeposited, NewOnhand) values ('10-feb-2021 08:15', 1500, 300, 0, 1200);
insert into transactions (aDateTime, OnHand, AmountWithDrawn, AmountDeposited, NewOnhand) values ('01-mar-2021 08:15', 1200, 0, 1000, 2200);
insert into transactions (aDateTime, OnHand, AmountWithDrawn, AmountDeposited, NewOnhand) values ('07-mar-2021 08:15', 2200, 400, 0, 1800);
insert into transactions (aDateTime, OnHand, AmountWithDrawn, AmountDeposited, NewOnhand) values ('11-mar-2021 08:15', 1800, 500, 0, 1300);
List:
select * from transactions order by id
Now we want to group per month:
select distinct month(adatetime) aMonth, sum(amountWithDrawn) SumWithDrawn, sum(amountDeposited) SumDeposited from transactions group by month(adatetime);
But how can we get Onhand and NewOnhand in the query?
with cte as
(select distinct month(adatetime) aMonth, sum(amountWithDrawn) SumWithDrawn, sum(amountDeposited) SumDeposited, min(id) MinId, max(id) MaxId from transactions group by month(adatetime))
select aMonth, (select Onhand from transactions where id = minid) FirstOnhand, SumWithDrawn, SumDeposited, (select NewOnhand from transactions where id = maxid) LastNewOnhand from cte
This way would be quite efficient (add index to ID) and the code quite is easy to read and comprehend.
Upvotes: 0
Reputation: 19
How to two query 'UNION'
SELECT TOP 1 EmployeeId, AttendenceId, Intime
FROM EmployeeAttendence
WHERE AttendenceDate >='1/18/2020 00:00:00'
AND AttendenceDate <='1/18/2020 23:59:59'
GROUP BY EmployeeId,AttendenceId,Intime
ORDER BY AttendenceId
SELECT TOP 1 EmployeeId, AttendenceId, OutTime
FROM EmployeeAttendence
WHERE AttendenceDate >='1/18/2020 00:00:00'
AND AttendenceDate <='1/18/2020 23:59:59'
GROUP BY EmployeeId, AttendenceId, OutTime
ORDER BY AttendenceId desc
Upvotes: 0
Reputation: 1
select tt.id, tt.groups, tt.val, x.sstart, tt.[end] from test_table tt join
(Select groups,First_value(start) over (partition by groups order by groups) sstart from test_table
Union
Select groups,Last_Value(start) over (partition by groups order by groups) sstart from test_table) as x
on tt.start=x.sstart
Order by tt.groups, sstart Desc
Upvotes: -1
Reputation: 13248
This is one way -
select t.*
from tbl t
join (
select [group],
min(val) as val_1,
max(val) as val_2
from tbl
group by [group]
) v
on t.[group] = v.[group]
and (t.val = v.val_1
or t.val = v.val_2);
Fiddle: http://sqlfiddle.com/#!3/c682f/1/0
Another approach:
select id, [group], val, [start], [end]
from(
select t.*,
max(val) over(partition by [group]) as max_grp,
min(val) over(partition by [group]) as min_grp
from tbl t
) x
where val in (max_grp,min_grp)
Upvotes: 6
Reputation: 31879
How about using ROW_NUMBER
:
WITH Cte AS(
SELECT *,
RnAsc = ROW_NUMBER() OVER(PARTITION BY [group] ORDER BY val),
RnDesc = ROW_NUMBER() OVER(PARTITION BY [group] ORDER BY val DESC)
FROM tbl
)
SELECT
id, [group], val, start, [end]
FROM Cte
WHERE
RnAsc = 1 OR RnDesc = 1
ORDER BY [group], val
Upvotes: 16