Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Selecting first row per group

I have a table with three columns: A,B,C.

The values are:

+---+-----+----+
| A |  B  | C  |
+---+-----+----+
| 1 | -10 |  5 |
| 1 |   0 |  5 |
| 1 |  10 |  5 |
| 2 |  10 | 12 |
| 2 |   0 | 12 |
| 3 | -10 | 14 |
| 4 |   0 |  8 |
| 4 |  10 |  8 |
| 5 |   0 |  6 |
| 5 |   1 |  6 |
| 5 |  -5 |  6 |
+---+-----+----+

If I first order the data by column A, then column B, then column C (although I did make all column C values the same per column A value) how would I select the "first row" per column A?

So, this should result in:

+---+-----+----+
| A |  B  | C  |
+---+-----+----+
| 1 | -10 |  5 |
| 2 |   0 | 12 |
| 3 | -10 | 14 |
| 4 |   0 |  8 |
| 5 |  -5 |  6 |
+---+-----+----+
like image 462
mezamorphic Avatar asked Jun 07 '12 10:06

mezamorphic


People also ask

How do you find the first value of each group?

groupby. nth() function is used to get the value corresponding the nth row for each group. To get the first value in a group, pass 0 as an argument to the nth() function.

How do I SELECT only the first row?

To return only the first row that matches your SELECT query, you need to add the LIMIT clause to your SELECT statement. The LIMIT clause is used to control the number of rows returned by your query. When you add LIMIT 1 to the SELECT statement, then only one row will be returned.


2 Answers

SELECT  a, b, c
FROM    (
        SELECT  *, ROW_NUMBER() OVER (PARTITION BY a ORDER BY b, c) rn
        FROM    mytable
        ) q
WHERE   rn = 1
ORDER BY
        a

or

SELECT  mi.*
FROM    (
        SELECT  DISTINCT  a
        FROM    mytable
        ) md
CROSS APPLY
        (
        SELECT  TOP 1 *
        FROM    mytable mi
        WHERE   mi.a = md.a
        ORDER BY
                b, c
        ) mi
ORDER BY
        a

Create a composite index on (a, b, c) for the queries to work faster.

Which one is more efficient depends on your data distribution.

If you have few distinct values of a but lots of records within each a, the second query would be better.

You could improve it even more by creating an indexed view:

CREATE VIEW v_mytable_da
WITH   SCHEMABINDING
AS
       SELECT  a, COUNT_BIG(*) cnt
       FROM    dbo.mytable
       GROUP BY
               a

GO

CREATE UNIQUE CLUSTERED INDEX
       pk_vmytableda_a
ON     v_mytable_da (a)

GO

SELECT  mi.*
FROM    v_mytable_da md
CROSS APPLY
        (
        SELECT  TOP 1 *
        FROM    mytable mi
        WHERE   mi.a = md.a
        ORDER BY
                b, c
        ) mi
ORDER BY
        a
like image 152
Quassnoi Avatar answered Dec 22 '22 01:12

Quassnoi


SELECT *
FROM
  (SELECT *,
          ROW_NUMBER() OVER (PARTITION BY Dealld
                             ORDER BY Price, Dealld) rn
   FROM DealOffers) q
WHERE rn = 1
ORDER BY Name
like image 45
Anil Kumar Avatar answered Dec 22 '22 01:12

Anil Kumar