[sql-server] SQL Server: UPDATE a table by using ORDER BY

I would like to know if there is a way to use an order by clause when updating a table. I am updating a table and setting a consecutive number, that's why the order of the update is important. Using the following sql statement, I was able to solve it without using a cursor:

DECLARE @Number INT = 0

UPDATE Test
SET @Number = Number = @Number +1

now what I'd like to to do is an order by clause like so:

 DECLARE @Number INT = 0

 UPDATE Test
 SET @Number = Number = @Number +1
 ORDER BY Test.Id DESC

I've read: How to update and order by using ms sql The solutions to this question do not solve the ordering problem - they just filter the items on which the update is applied.

Take care, Martin

This question is related to sql-server

The answer is


I had a similar problem and solved it using ROW_NUMBER() in combination with the OVER keyword. The task was to retrospectively populate a new TicketNo (integer) field in a simple table based on the original CreatedDate, and grouped by ModuleId - so that ticket numbers started at 1 within each Module group and incremented by date. The table already had a TicketID primary key (a GUID).

Here's the SQL:

UPDATE Tickets SET TicketNo=T2.RowNo
FROM Tickets
INNER JOIN 
  (select TicketID, TicketNo, 
     ROW_NUMBER() OVER (PARTITION BY ModuleId ORDER BY DateCreated) AS RowNo from Tickets) 
  AS T2 ON T2.TicketID = Tickets.TicketID

Worked a treat!


update based on Ordering by the order of values in a SQL IN() clause

Solution:

DECLARE @counter int
SET @counter = 0

;WITH q  AS
        (
select * from Products WHERE ID in (SELECT TOP (10) ID FROM Products WHERE  ID IN( 3,2,1) 
ORDER BY ID DESC)
        )
update q set Display= @counter, @counter = @counter + 1

This updates based on descending 3,2,1

Hope helps someone.


I ran into the same problem and was able to resolve it in very powerful way that allows unlimited sorting possibilities.

I created a View using (saving) 2 sort orders (*explanation on how to do so below).

After that I simply applied the update queries to the View created and it worked great.

Here are the 2 queries I used on the view:

1st Query:

Update  MyView
Set SortID=0


2nd Query:

DECLARE @sortID int
SET     @sortID = 0
UPDATE  MyView
SET     @sortID = sortID = @sortID + 1


*To be able to save the sorting on the View I put TOP into the SELECT statement. This very useful workaround allows the View results to be returned sorted as set when the View was created when the View is opened. In my case it looked like:

(NOTE: Using this workaround will place an big load on the server if using a large table and it is therefore recommended to include as few fields as possible in the view if working with large tables)

SELECT     TOP (600000) 
dbo.Items.ID, dbo.Items.Code, dbo.Items.SortID, dbo.Supplier.Date, 
dbo.Supplier.Code AS Expr1
FROM         dbo.Items INNER JOIN
                      dbo.Supplier ON dbo.Items.SupplierCode = dbo.Supplier.Code
ORDER BY dbo.Supplier.Date, dbo.Items.ID DESC



Running: SQL Server 2005 on a Windows Server 2003

Additional Keywords: How to Update a SQL column with Ascending or Descending Numbers - Numeric Values / how to set order in SQL update statement / how to save order by in sql view / increment sql update / auto autoincrement sql update / create sql field with ascending numbers


You can not use ORDER BY as part of the UPDATE statement (you can use in sub-selects that are part of the update).

UPDATE Test 
SET Number = rowNumber 
FROM Test
INNER JOIN 
(SELECT ID, row_number() OVER (ORDER BY ID DESC) as rowNumber
FROM Test) drRowNumbers ON drRowNumbers.ID = Test.ID

Edit

Following solution could have problems with clustered indexes involved as mentioned here. Thanks to Martin for pointing this out.

The answer is kept to educate those (like me) who don't know all side-effects or ins and outs of SQL Server.


Expanding on the answer gaven by Quassnoi in your link, following works

DECLARE @Test TABLE (Number INTEGER, AText VARCHAR(2), ID INTEGER)
DECLARE @Number INT

INSERT INTO @Test VALUES (1, 'A', 1)
INSERT INTO @Test VALUES (2, 'B', 2)
INSERT INTO @Test VALUES (1, 'E', 5)
INSERT INTO @Test VALUES (3, 'C', 3)
INSERT INTO @Test VALUES (2, 'D', 4)

SET @Number = 0

;WITH q AS (
    SELECT  TOP 1000000 *
    FROM    @Test
    ORDER BY
            ID
)            
UPDATE  q
SET     @Number = Number = @Number + 1

SET @pos := 0;
UPDATE TABLE_NAME SET Roll_No = ( SELECT @pos := @pos + 1 ) ORDER BY First_Name ASC;

In the above example query simply update the student Roll_No column depending on the student Frist_Name column. From 1 to No_of_records in the table. I hope it's clear now.


The row_number() function would be the best approach to this problem.

UPDATE T
    SET T.Number = R.rowNum
    FROM Test T
    JOIN (
        SELECT T2.id,row_number() over (order by T2.Id desc) rowNum from Test T2
    ) R on T.id=R.id 

IF OBJECT_ID('tempdb..#TAB') IS NOT NULL
BEGIN
    DROP TABLE #TAB
END

CREATE TABLE #TAB(CH1 INT,CH2 INT,CH3 INT)

DECLARE @CH2 INT = NULL , @CH3 INT=NULL,@SPID INT=NULL,@SQL NVARCHAR(4000)='', @ParmDefinition NVARCHAR(50)= '',
@RET_MESSAGE AS VARCHAR(8000)='',@RET_ERROR INT=0


SET @ParmDefinition='@SPID INT,@CH2 INT OUTPUT,@CH3 INT OUTPUT'

SET @SQL='UPDATE T
            SET CH1=@SPID,@CH2= T.CH2,@CH3= T.CH3
            FROM #TAB T WITH(ROWLOCK)
            INNER JOIN (
                        SELECT TOP(1)  CH1,CH2,CH3
                        FROM
                        #TAB WITH(NOLOCK)
                        WHERE CH1 IS NULL
                        ORDER BY CH2 DESC) V ON T.CH2= V.CH2 AND T.CH3= V.CH3' 

INSERT INTO #TAB
(CH2 ,CH3 )
SELECT 1,2 UNION ALL
SELECT 2,3 UNION ALL
SELECT 3,4

BEGIN TRY
    WHILE EXISTS(SELECT TOP 1 1 FROM #TAB WHERE CH1 IS NULL)
    BEGIN

        EXECUTE @RET_ERROR = sp_executesql @SQL, @ParmDefinition,@SPID =@@SPID,  @CH2=@CH2 OUTPUT,@CH3=@CH3 OUTPUT;  

        SELECT * FROM #TAB
        SELECT @CH2,@CH3

    END

END TRY
BEGIN CATCH

    SET @RET_ERROR=ERROR_NUMBER()
    SET @RET_MESSAGE =  '@ERROR_NUMBER : ' + CAST(ERROR_NUMBER()  AS VARCHAR(255)) + '@ERROR_SEVERITY  :' + CAST( ERROR_SEVERITY()  AS VARCHAR(255)) 
    + '@ERROR_STATE :' + CAST(ERROR_STATE() AS VARCHAR(255)) + '@ERROR_LINE :' + CAST( ERROR_LINE() AS VARCHAR(255)) 
    +  '@ERROR_MESSAGE :' + ERROR_MESSAGE()  ;

    SELECT @RET_ERROR,@RET_MESSAGE;

END CATCH