Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting data from first and last row of each group

I've found many similar topics to this but none I can understand well enough to solve my specific case.

A have a table with the following basic structure:

+------------------------+
| id | session ID | bal  |
+------------------------+
| 0  | 00000002 | 100    |
| 1  | 00000002 | 120    |
| 2  | 00000002 | 140    |
| 3  | 00000001 | 900    |
| 4  | 00000001 | 800    |
| 5  | 00000001 | 500    |
+------------------------+ 

I need to create a (Microsoft SQL) query which returns each unique sessionID along with the first ("start") and last ("end") bal entries based on sequencial value of the ID column. The result would look like this:

+---------------------------+
| session ID | start | end  |
+---------------------------+
| 00000002   | 100   | 140  |
| 00000001   | 900   | 500  |
+---------------------------+

How can I achieve this?

like image 626
user2769442 Avatar asked Sep 16 '14 13:09

user2769442


People also ask

How do I get the first row and last row in SQL?

To get the first and last record, use UNION. LIMIT is also used to get the number of records you want.

How do you find the latest record by GROUP BY?

The group by will always return the first record in the group on the result set. SELECT id, category_id, post_title FROM posts WHERE id IN ( SELECT MAX(id) FROM posts GROUP BY category_id ); This will return the posts with the highest IDs in each group. Save this answer.

How do you use ORDER BY and GROUP BY in a single query?

Using Group By and Order By Together When combining the Group By and Order By clauses, it is important to bear in mind that, in terms of placement within a SELECT statement: The GROUP BY clause is placed after the WHERE clause. The GROUP BY clause is placed before the ORDER BY clause.


2 Answers

EDIT In reply to your comment, SQL Server supports window functions. One way to look up the first and last bal values per Session ID is:

select  distinct [Session ID]
,       first_value(bal) over (partition by [Session ID] order by id) as [start]
,       first_value(bal) over (partition by [Session ID] order by id desc) as [end]
from    Table1

Example at SQL Fiddle.

Another way (there are many) is increasing and decreasing row numbers:

select  [Session ID]
,       max(case when rn1 = 1 then bal end) as [start]
,       max(case when rn2 = 1 then bal end) as [end]
from    (
        select  row_number() over (partition by [Session ID] order by id) as rn1
        ,       row_number() over (partition by [Session ID] order by id desc) as rn2
        ,       *
        from    Table1
        ) as SubQueryAlias
group by
        [Session ID]

Example at SQL Fiddle.

like image 197
Andomar Avatar answered Sep 23 '22 11:09

Andomar


You can use JOIN and Common Table Expression for readability:

with CTE as
(
    select 
        sessionId, min(id) as firstId, max(id) as lastId
    from 
        log
    group by sessionId
)
select
    CTE.sessionId, Log1.bal as start, Log2.bal as [end]
from
    CTE
    join Log as Log1 on Log1.id = CTE.firstId
    join Log as Log2 on Log2.id = CTE.lastId

See the SQL Fiddle.

like image 43
Caffé Avatar answered Sep 25 '22 11:09

Caffé