views:

85

answers:

5

I need to be able to select only the first row for each name that has the greatest value.

I have a table with the following:

id  name  value

0   JOHN  123
1   STEVE 125
2   JOHN  127
3   JOHN  126

So I am looking to return:

id  name  value

1   STEVE 125
2   JOHN  127

Any idea on the MSSQL Syntax on how to perform this operation?

A: 

How about:

SELECT a.id, a.name, b.maxvalue
  FROM mytbl a
 INNER JOIN (SELECT id, max(value) as maxvalue
               FROM mytbl
           GROUP BY id) b ON b.id = a.id
Justin Ethier
Since the `id` is in the `GROUP BY` as well, wouldn't this return all records? I'm thinking a self-join to get the `id` may be necessary and just `GROUP BY` on `name` at first and then `JOIN` to get the `id`
Daniel DiPaolo
+2  A: 

try:

SELECT
    MIN(id) as id,dt.name,dt.value
    FROM (SELECT
              name,MAX(value) as value
              FROM YourTable
              GROUP BY name
         ) dt
        INNER JOIN YourTable t ON dt.name=t.name and dt.value=t.value
    GROUP BY dt.name,dt.value

try it out:

DECLARE @YourTable table (id int, name varchar(10), value int)
INSERT @YourTable VALUES (0, 'JOHN',  123)
INSERT @YourTable VALUES (1, 'STEVE', 125)
INSERT @YourTable VALUES (2, 'JOHN',  127)
INSERT @YourTable VALUES (3, 'JOHN',  126)

--extra data not in the question, shows why you need the outer group by
INSERT @YourTable VALUES (4, 'JOHN',  127)
INSERT @YourTable VALUES (5, 'JOHN',  127)
INSERT @YourTable VALUES (6, 'JOHN',  127)
INSERT @YourTable VALUES (7, 'JOHN',  127)

SELECT
    MIN(id) as id,dt.name,dt.value
    FROM (SELECT
              name,MAX(value) as value
              FROM @YourTable
              GROUP BY name
         ) dt
        INNER JOIN @YourTable t ON dt.name=t.name and dt.value=t.value
    GROUP BY dt.name,dt.value
    ORDER BY id

output:

id          name       value
----------- ---------- -----------
1           STEVE      125
2           JOHN       127

(2 row(s) affected)
KM
Doesn't return the `id`
Daniel DiPaolo
it does return the id now!
KM
+6  A: 

While you specified SQL Server, you did not specify the version. If you are using SQL Server 2005 or later, you can do something like:

With RankedItems As
    (
        Select id, name, value
            , Row_Number() Over ( Partition By name Order By value Desc, id Asc ) As ItemRank
        From Table
    )
Select id, name, value
From RankedItems
Where ItemRank = 1
Thomas
Best answer IMO, +1
ajdams
The ranking functions make these kinds of problems much easier to solve.
Philip Kelley
This can be accomplished without using a CTE. Admittedly, either solution will result in the same underlying query.
Jeremiah Peschka
small type-o, there is NO `FROM` in the CTE, and the `ORDER BY` should be `Value DESC, id ASC`
KM
@KM - Fixed the missing from clause. Technically the OP did not explicitly state a sort order on the final result but I suppose I can throw that in too.
Thomas
@KM - If by the order by you mean the one used on Row_Number, that would depend on the rule in the case of ties. I.e., if "John" had two entries with the same value, should both be returned or should the one with the higher id trump? Not clear in the OP.
Thomas
@Thomas, I was talking about the ORDER BY used for the ItemRank. OP wants the lowest ID, your ItemRank will order them wrong (without the `Value DESC, id ASC`) if the rows are not inserted from lowest to highest id.
KM
@KM - I don't see that in the OP although you could argue it is implied. I'll buy it. I've updated my answer to reflect that.
Thomas
@KM - All that would happen without the additional `id asc` sorting is that the query would arbitrarily choose one of the rows with the highest value for a given name. So if id=27 had the highest value for Bob and so did id=5, you might get back id=27. If it the lowest id should trump in a tie, then yes, the system should indicate that with the additional sorting.
Thomas
A: 
SELECT a.id, a.name, a.value
  FROM mytbl a
 INNER JOIN (SELECT name, max(value) as maxvalue
               FROM mytbl
           GROUP BY name) b ON b.name = a.name and b.maxvalue = a.value
shahkalpesh
+2  A: 

You could do something like

SELECT id, name, value
FROM (SELECT id, name, value
             ROWNUMBER() OVER (PARTITION BY name ORDER BY value DESC) AS r
      FROM table) AS x
WHERE x.r = 1 ;

This will not work in SQL Server 2000 and earlier, but it will be incredibly fast in SQL Server 2005 and 2008

Jeremiah Peschka