How do I select the first row per group in an SQL Query?

ぐ巨炮叔叔 提交于 2019-11-30 05:14:51
declare @sometable table ( foo int, bar int, value int )

insert into @sometable values (47, 1, 100)
insert into @sometable values (47, 0, 10)
insert into @sometable values (47, 2, 10)
insert into @sometable values (46, 0, 100)
insert into @sometable values (46, 1, 10)
insert into @sometable values (46, 2, 10)
insert into @sometable values (44, 0, 2)

;WITH cte AS 
(
    SELECT   Foo, Bar, SUM(value) AS SumValue, ROW_NUMBER() OVER(PARTITION BY Foo ORDER BY FOO DESC, SUM(value) DESC) AS RowNumber
    FROM     @SomeTable
    GROUP BY Foo, Bar
)
SELECT * 
FROM cte
WHERE RowNumber = 1

I might disagree with rjmunru in that using Ansii style joins can often be easier to read than subqueries but to each his own -- I just follow what our DBAs say to do.

If you just want the first result from a query, you might be able to use a rownum (if using oracle, other databases probably have something similiar).

select * from foo_t f where f.bar = 'bleh' and rownum = 1

Of course a HAVING clause might also be appropriate, depending on what you are trying to do.

"HAVING is used to perform an action on groups created by GROUP BY similar to that of the WHERE clause on rows in a basic SQL statement. The WHERE clause limits the rows evaluated. The HAVING clause limits the grouped rows returned."

hth

Just group on Players.Nick alone, and select the first (min) of the description

SELECT     Players.Nick, MIN(Reasons.Description), SUM(Marks.Value) AS Sum
FROM         Marks INNER JOIN
                      Players ON Marks.PlayerID = Players.ID INNER JOIN
                      Reasons ON Marks.ReasonId = Reasons.ID
GROUP BY Players.Nick
ORDER BY Players.Nick, Sum DESC

that is if you always want the first without knowing it

Gerauld

It's an old post but I had the same problem today. I've solved it by trying many queries until it works. I'm using SQL Compact 3.5 with visual basic 2010.

This example is for a table named "TESTMAX" with columns "Id" (primary key), "nom" (name) and "value", you can use this to obtain rows with max "value" for each "nom" :

SELECT TESTMAX.Id, TESTMAX.NOM, TESTMAX.Value
FROM     TESTMAX INNER JOIN
                  TESTMAX AS TESTMAX_1 ON TESTMAX.NOM = TESTMAX_1.NOM
WHERE  (TESTMAX.Value IN
                      (SELECT MAX(Value) AS Expr1
                       FROM      TESTMAX AS TESTMAX_2
                       WHERE   (NOM = TESTMAX_1.NOM)))
GROUP BY TESTMAX.Id, TESTMAX.NOM, TESTMAX.Value

If you want to delete the other rows, you can also use :

DELETE FROM TESTMAX
WHERE  (Id NOT IN
                      (SELECT TESTMAX_3.Id
                       FROM      TESTMAX AS TESTMAX_3 INNER JOIN
                                         TESTMAX AS TESTMAX_1 ON TESTMAX_3.NOM = TESTMAX_1.NOM
                       WHERE   (TESTMAX_3.Value IN
                                             (SELECT MAX(Value) AS Expr1
                                              FROM      TESTMAX AS TESTMAX_2
                                              WHERE   (NOM = TESTMAX_1.NOM)))
                       GROUP BY TESTMAX_3.Id, TESTMAX_3.NOM, TESTMAX_3.Value))

In general, try using Subqueries rather than joining and grouping - it often makes SQL that is much easier to understand.

SELECT Nick,
   (SELECT Description from Reasons WHERE Reasons.ID = (
       SELECT FIRST(Marks.ReasonId) from Marks WHERE Marks.PlayerID = Players.ID)
   ),
   (SELECT SUM(Value) from Marks WHERE Marks.PlayerID = Players.ID)

Is this an opportunity to use a 'HAVING' clause ? (You want to discriminate on an aggregate function - 'Sum') ?

(EDITED Based on edited question) Then, since you wish to filter based on the value of an aggregated column, what you need is a Having Clause.

  SELECT p.Nick, r.Description, SUM(m.Value) Sum
  FROM Marks m
    JOIN Players p
      ON m.PlayerID = p.ID 
    JOIN Reasons r 
      ON m.ReasonId = r.ID
  GROUP BY p.Nick, r.Description
  Having SUM(m.Value) =
      (Select Max(Sum) From
        (SELECT SUM(m.Value) Sum
         FROM Marks mi
           JOIN Players pi
              ON mi.PlayerID = pi.ID 
           JOIN Reasons r i
             ON mi.ReasonId = ri.ID
         Where pi.Nick = p.Nick
         GROUP BY pi.Nick, ri.Description))

  Order By p.Nick, Sum Desc

Curious. Only way I could get this to work was by using a temporary holding table in memory. (TSQL syntax)

-- original test data
declare @sometable table ( foo int, bar int, value int )

insert into @sometable values (1, 5, 10)
insert into @sometable values (1, 4, 20)
insert into @sometable values (2, 1, 1)
insert into @sometable values (2, 1, 10)
insert into @sometable values (2, 1, 1)
insert into @sometable values (2, 2, 13)
insert into @sometable values (3, 4, 25)
insert into @sometable values (3, 5, 1)
insert into @sometable values (3, 1, 1)
insert into @sometable values (3, 1, 1)
insert into @sometable values (3, 1, 1)
insert into @sometable values (3, 1, 1)
insert into @sometable values (3, 1, 1)

-- temp table for initial aggregation
declare @t2 table (foo int, bar int, sums int)
insert into @t2
select foo, bar, sum(value) 
from @sometable
group by foo, bar

-- final result
select foo, bar, sums
from @t2 a
where sums = 
    (select max(sums) from @t2 b 
     where b.foo = a.foo)

SQL Server 2005 you can use this:

declare @sometable table ( foo int, bar int, value int )

insert into @sometable values (1, 5, 10) insert into @sometable values (1, 4, 20) insert into @sometable values (2, 1, 1) insert into @sometable values (2, 1, 10) insert into @sometable values (2, 1, 1) insert into @sometable values (2, 2, 13) insert into @sometable values (3, 4, 25) insert into @sometable values (3, 5, 1) insert into @sometable values (3, 1, 1) insert into @sometable values (3, 1, 1) insert into @sometable values (3, 1, 1) insert into @sometable values (3, 1, 1) insert into @sometable values (3, 1, 1)

-- temp table for initial aggregation declare @t2 table (foo int, bar int, sums int) insert into @t2 select foo, bar, sum(value) from @sometable group by foo, bar

select * from ( SELECT foo, bar, sums, ROW_NUMBER() OVER (PARTITION BY Foo ORDER BY Sums DESC) ROWNO FROM @t2) x where x.ROWNO = 1

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!