views:

65

answers:

3

I have a table, with the following columns:

PK1   PK2   ID   DATE    Value  flag

I do a calculation that involves taking the max value per ID.

  select id,
         max(value) 
    from table 
group by id

I want to mark the flag on the rows that I am using. If id and the max(value) correspond to multiple rows flag the one with the max date. If they have the same id,max(value) and max(date) flag exactly one of those rows (don't care which at that point)

Any ideas?

Thanks!

+7  A: 

For SQL2005+ maybe something like this. (Assuming that "Mark" means update the flag column)

WITH cte AS
(
SELECT PK1, PK2, ID, DATE, Value, flag,
ROW_NUMBER() OVER (PARTITION BY id ORDER BY value desc, date desc) AS RN
FROM table 
)
UPDATE cte
SET flag=CASE WHEN RN=1 THEN 1 ELSE 0 END
Martin Smith
Beat me to it. :)
Will A
Does that work to use the CTE reference as the table to be updated?
OMG Ponies
what is cte????
kralco626
Nice - I've never seen an update to a CTE before. This is definitely going in my repertoire.
Matt
@OMG Yes. You can delete as well which allows duplicates to be removed on a table without a Primary Key using `row_number` http://www.kodyaz.com/articles/delete-duplicate-records-rows-in-a-table.aspx
Martin Smith
@kralco626 a CTE is a "Common Table Expression". It is similar to a derived table or a View.
Martin Smith
OMG Ponies
@OMG - Yes I presume that it will have the same limitations as an Updatable View. I read the other day that you can Update an inline table valued function as well (that was news to me at least!)
Martin Smith
News to me too - thx!
OMG Ponies
any idea why the slightly alternitive solution i posted does not work?
kralco626
I tested your solution. It works. Still wanna know why what I posted does not work...
kralco626
@kralco626 - I posted a comment to your answer. It would probably help to run just the `SELECT .. ROW_NUMBER()' query on its own so you see what it returns.
Martin Smith
Thanks for your help Martin. I think I really understand how to use partition now. This is the second time I have run into it and the first time around I really had no clue
kralco626
+5  A: 

With SQL Server 2005 or above, you can do the following:

SELECT x.*
FROM (
 SELECT *,
        ROW_NUMBER() OVER (PARTITION BY id ORDER BY value DESC, date DESC) AS RN
 FROM table
) x
WHERE x.RN = 1
Will A
Msg 156, Level 15, State 1, Line 7Incorrect syntax near the keyword 'WHERE'.
kralco626
@kralco626: See update - SQL Server requires an alias for a subquery/derived table/inline view
OMG Ponies
oh, outside the ). I tried putting an alias after table. Thanks!
kralco626
@OMG - thanks - without actually running the T-SQL in SSMS I'm prone to syntax errors. :)
Will A
+1  A: 

Why does this NOT work?

update table
set flag = '1'
where id in (select id from (SELECT PK1, PK2, id, date,value, 
        ROW_NUMBER() OVER (PARTITION BY id ORDER BY value DESC, date DESC) AS RN 
FROM table) t where RN = 1)

EDIT: the below statement WILL work if you don't want to (or can't in some sql versions) use a cte

The above will not work because, as martin says below, the id is still in the list.

however, the below will work if someone prefereds not to use a cte.(Not nearly as elegant as Martin's solution though)

update table
set flag = '1'
where convert(varchar,PK1)+convert(varchar,PK2) in (select convert(varchar,PK1)+convert(varchar,PK2) from (SELECT PK1, PK2, id, date,value, 
        ROW_NUMBER() OVER (PARTITION BY id ORDER BY value DESC, date DESC) AS RN 
FROM table) t where RN = 1)
kralco626
This should be an update to your question not an answer. The query you have posted here though doesn't make any sense. You are partitioning by id and all ids present in the data will have a row_number with a value of 1. so the filter `where RN = 1` will never remove any ids. so you are doing `where id in (list of all ids)`. (If you run it as a Select on its own you should see what I mean)
Martin Smith
@martin - didn't think of updating my answer... I though of putting it as a comment and desided that wouldnt work. Next time i'll update my answer. Ya i see what your saying, because the site_id is in the list it will update all of them because it dosn't know which one.
kralco626
@martin - edited the above with a non-cte solution. Not as cool as yours though. and it does not set the flag to 0 if it is not in the list.
kralco626