tags:

views:

125

answers:

6

Hello:

I was surprised to see that the following updated 34 rows...not 5:

UPDATE 
 Message
 SET StatusRawCode = 25
WHERE StatusRawCode in
  ( 
     Select TOP 5 
      M2.StatusRawCode
       From Message as M2
        Where M2.StatusRawCode = 5
  )

Any ideas on how to pound this into the right shape?

Thank you!

+2  A: 

Is there a way to distinctly identify the TOP 5 rows?

As per your query, it doesn't matter if you do TOP 5 (because you are selecting records with StatusRawCode = 5). So, in a way your query is same as

UPDATE 
 Message
 SET StatusRawCode = 25
WHERE StatusRawCode = 5
shahkalpesh
+4  A: 

My guess is the StatusRawCode values returned from your sub query are values used in the 34 records that were updated. Instead of

WHERE StatusRawCode IN

Use this:

UPDATE 
    Message
SET StatusRawCode = 25
    WHERE PrimaryKey in
    ( 
        Select TOP 5 
            PrimaryKey
        From Message as M2
        Where M2.StatusRawCode = 5
    )

In essence, you will be selecting the primary keys of the 5 rows to be updated in the sub query. Keep in mind this will update only the top 5 records based on the clustered index ordering of your table. You will want to add an order by clause if you need to specify a specific criteria for the TOP 5 records.

For example, if there is a column called Rank that you want to use as the criteria, write your query like so:

UPDATE 
    Message
SET 
    StatusRawCode = 25
WHERE 
    PrimaryKey IN
    ( 
        SELECT TOP 5 
            PrimaryKey
        FROM 
            Message as M2
        WHERE 
            M2.StatusRawCode = 5
        ORDER BY
            Rank DESC
    )

This will give you the TOP 5 records based on the Rank column values. You can substitute for your column as necessary.

KG
That's it! The clause was accepting anything...
+2  A: 

It appears that StatusRawCode is far from unique. You want to pull back the primary key, or another unique column to identify the top five. You're updating any row where StatusRawCode is equal to 5. Apparently, there are 34 rows which meet that condition.

Moreover, top 5 will only mean anything with an order by clause. SQL Server does not store rows in any particular order, and you will not be guaranteed to get the same five rows back every time. SQL Server stores rows in 8k pages, and it does not guarantee nor provide a consistent order for your row set. You cannot rely on this, and you must use an order by to ensure that you're getting the correct five rows. Otherwise, you'll be updating five random rows.

Eric
Thanks for that clarification. I'll add an Order By clause.
+1  A: 

You need to put the IN condition on an unique, primary key.

In SQL 2K5 and forward you can also use a CTE:

  WITH cte AS (
     Select TOP 5 
      M2.StatusRawCode
       From Message as M2
        Where M2.StatusRawCode = 5
    ORDER BY ...
  )
  UPDATE cte 
  SET StatusRawCode = 25
Remus Rusanu
A: 

I'd like to pitch in and recommend that you always start an UPDATE statement with SELECT statement. Like this:

SELECT * 
--UPDATE m SET StatusRawCode = 25
FROM Message m
WHERE StatusRawCode in  (
        Select TOP 5
       M2.StatusRawCode
       From Message as M2
       Where M2.StatusRawCode = 5  
);

...but definitely fix up your query to your actual requirements, once you can see where you're going wrong (which in this case, would probably be similar to KG's response)

This will show you the rows that will be affected by your query... so once you have this correct, change the SELECT * for the UPDATE row (currently commented), and you should get predictable results.

Bear in mind though, that UPDATE doesn't support ORDER BY, so if you end up trying UPDATE TOP (5)..., then you won't get the results you want.

Rob

Rob Farley
A: 

If you don't have a primary key and CTEs are not available, the following will also work:

UPDATE M
SET StatusRawCode = 25
FROM (Select TOP 5 
    M2.StatusRawCode
    FROM Message as M2
    WHERE M2.StatusRawCode = 5
    ORDER BY ...) M
Shannon Severance