使用row_number()进行SQL更新


113

我想用递增编号更新列CODE_DEST。我有:

CODE_DEST   RS_NOM
null        qsdf
null        sdfqsdfqsdf
null        qsdfqsdf

我想将其更新为:

CODE_DEST   RS_NOM
1           qsdf
2           sdfqsdfqsdf
3           qsdfqsdf

我已经试过这段代码:

UPDATE DESTINATAIRE_TEMP
SET CODE_DEST = TheId 
FROM (SELECT  Row_Number()   OVER (ORDER BY [RS_NOM]) AS TheId FROM DESTINATAIRE_TEMP)

这是行不通的,因为 )

我也尝试过:

WITH DESTINATAIRE_TEMP AS
  (
    SELECT 
    ROW_NUMBER() OVER (ORDER BY [RS_NOM] DESC) AS RN
    FROM DESTINATAIRE_TEMP
  )
UPDATE DESTINATAIRE_TEMP SET CODE_DEST=RN

但是,由于工会,这也不起作用。

如何使用ROW_NUMBER()SQL Server 2008 R2中的功能更新列?


发布样本数据和预期结果,这是获得SQL答案的最佳方法。否则你的?没有道理,会给出这样的答案UPDATE myCol = myCol+1 FROM MyTable WHERE ID=@MyID
JonH

Answers:


189

另一种选择

UPDATE x
SET x.CODE_DEST = x.New_CODE_DEST
FROM (
      SELECT CODE_DEST, ROW_NUMBER() OVER (ORDER BY [RS_NOM]) AS New_CODE_DEST
      FROM DESTINATAIRE_TEMP
      ) x

这是一个绝妙的答案,还是SQL Server可以在嵌套的SELECT中更新呢?我认为两者都是....
Bigjim,2013年

您可以使用WHERE子句极大地提高未来的更新性能(请参阅基于此的答案)
Simon_Weaver


46
With UpdateData  As
(
SELECT RS_NOM,
ROW_NUMBER() OVER (ORDER BY [RS_NOM] DESC) AS RN
FROM DESTINATAIRE_TEMP
)
UPDATE DESTINATAIRE_TEMP SET CODE_DEST = RN
FROM DESTINATAIRE_TEMP
INNER JOIN UpdateData ON DESTINATAIRE_TEMP.RS_NOM = UpdateData.RS_NOM

3
这仅在RS_NOM列中的值唯一时才有效,不是吗?我怀疑这是可以假设的。
Toby

17

您的第二次尝试失败主要是因为您将CTE命名为与基础表相同,并使CTE看起来像是递归CTE,因为它实际上引用了自身。一个递归CTE必须有其需要使用的具体结构UNION ALL集合运算符。

相反,您可以给CTE一个不同的名称,并为其添加目标列:

With SomeName As
(
SELECT 
CODE_DEST,
ROW_NUMBER() OVER (ORDER BY [RS_NOM] DESC) AS RN
FROM DESTINATAIRE_TEMP
)
UPDATE SomeName SET CODE_DEST=RN

16

这是@Aleksandr Fedorenko的答案的修改版本,其中添加了WHERE子句:

UPDATE x
SET x.CODE_DEST = x.New_CODE_DEST
FROM (
      SELECT CODE_DEST, ROW_NUMBER() OVER (ORDER BY [RS_NOM]) AS New_CODE_DEST
      FROM DESTINATAIRE_TEMP
      ) x
WHERE x.CODE_DEST <> x.New_CODE_DEST AND x.CODE_DEST IS NOT NULL

通过添加WHERE子句,我发现以后的更新性能得到了极大的提高。即使该值已经存在,SQL Server似乎也会更新该行,并且这样做需要花费时间,因此添加where子句可使它仅跳过该值未更改的行。我不得不说我对它运行查询的速度感到惊讶。

免责声明:我不是DB专家,并且我为我的子句使用PARTITION BY,因此此查询的结果可能不完全相同。对我而言,该列是客户的已付款订单,因此设置后该值通常不会更改。

还要确保您具有索引,尤其是在SELECT语句上具有WHERE子句的情况下。过滤索引对我非常有用,因为我正在根据付款状态进行过滤。


我的查询使用PARTITION

UPDATE  UpdateTarget
SET     PaidOrderIndex = New_PaidOrderIndex
FROM
(
    SELECT  PaidOrderIndex, SimpleMembershipUserName, ROW_NUMBER() OVER(PARTITION BY SimpleMembershipUserName ORDER BY OrderId) AS New_PaidOrderIndex
    FROM    [Order]
    WHERE   PaymentStatusTypeId in (2,3,6) and SimpleMembershipUserName is not null
) AS UpdateTarget

WHERE UpdateTarget.PaidOrderIndex <> UpdateTarget.New_PaidOrderIndex AND UpdateTarget.PaidOrderIndex IS NOT NULL

-- test to 'break' some of the rows, and then run the UPDATE again
update [order] set PaidOrderIndex = 2 where PaidOrderIndex=3

如果列不可为空,则不需要“ IS NOT NULL”部分。


当我说性能提升很大时,我的意思是更新少量行时基本上是瞬时的。使用正确的索引,我能够获得与“内部”查询本身花费的时间相同的更新:

  SELECT  PaidOrderIndex, SimpleMembershipUserName, ROW_NUMBER() OVER(PARTITION BY SimpleMembershipUserName ORDER BY OrderId) AS New_PaidOrderIndex
    FROM    [Order]
    WHERE   PaymentStatusTypeId in (2,3,6) and SimpleMembershipUserName is not null

@AleksandrFedorenko,老实说,我对此感到惊讶,但很高兴它:)索引对我创建的索引也很重要
Simon_Weaver

1
感谢您的有用提示。最好的祝福。
Sedat Kumcu

3

我这样做是为了我的处境

WITH myUpdate (id, myRowNumber )
AS
( 
    SELECT id, ROW_NUMBER() over (order by ID) As myRowNumber
    FROM AspNetUsers
    WHERE  UserType='Customer' 
 )

update AspNetUsers set EmployeeCode = FORMAT(myRowNumber,'00000#') 
FROM myUpdate
    left join AspNetUsers u on u.Id=myUpdate.id

1

简便的更新光标的方法

UPDATE Cursor
SET Cursor.CODE = Cursor.New_CODE
FROM (
  SELECT CODE, ROW_NUMBER() OVER (ORDER BY [CODE]) AS New_CODE
  FROM Table Where CODE BETWEEN 1000 AND 1999
  ) Cursor

1

如果表没有关系,只需将所有行号复制到新表中,然后删除旧表,并用旧表重命名新表。

Select   RowNum = ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) , * INTO cdm.dbo.SALES2018 from 
(
select * from SALE2018) as SalesSource
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.