Atualização SQL com row_number ()

113

Quero atualizar minha coluna CODE_DEST com um número incremental. Eu tenho:

CODE_DEST   RS_NOM
null        qsdf
null        sdfqsdfqsdf
null        qsdfqsdf

Eu gostaria de atualizá-lo para ser:

CODE_DEST   RS_NOM
1           qsdf
2           sdfqsdfqsdf
3           qsdfqsdf

Eu tentei este código:

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

Isso não funciona por causa do )

Eu também tentei:

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

Mas isso também não funciona por causa da união.

Como posso atualizar uma coluna usando a ROW_NUMBER()função no SQL Server 2008 R2?

user609511
fonte
Publique dados de amostra e resultados esperados, essa é a melhor maneira de obter uma resposta SQL com. Caso contrário, o seu? não faz sentido e produzirá respostas como estaUPDATE myCol = myCol+1 FROM MyTable WHERE ID=@MyID
JonH

Respostas:

189

Mais uma opção

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
Aleksandr Fedorenko
fonte
Esta é uma resposta brilhante ou é brilhante que o SQL Server pode ser atualizado em um SELECT aninhado? Acho que ambos são ...
Bigjim
você pode melhorar enormemente o desempenho de atualizações futuras com uma cláusula WHERE (veja minha resposta com base nisso)
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
user609511
fonte
3
Isso só funciona se os valores na coluna RS_NOM forem únicos, não é? Duvido que isso possa ser assumido.
Toby
17

Sua segunda tentativa falhou principalmente porque você nomeou a CTE igual à tabela subjacente e fez a CTE parecer uma CTE recursiva , porque essencialmente se referia a si mesma. Um CTE recursivo deve ter uma estrutura específica que requer o uso do UNION ALLoperador de conjunto.

Em vez disso, você poderia simplesmente dar ao CTE um nome diferente, bem como adicionar a coluna de destino a ele:

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
Andriy M
fonte
16

Esta é uma versão modificada da resposta de @Aleksandr Fedorenko adicionando uma cláusula 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

Ao adicionar uma cláusula WHERE, descobri que o desempenho melhorou enormemente para as atualizações subsequentes. O Sql Server parece atualizar a linha, mesmo se o valor já existir e levar tempo para fazer isso, portanto, adicionar a cláusula where faz com que ele simplesmente pule as linhas onde o valor não mudou. Devo dizer que fiquei surpreso com a rapidez com que ele executou minha consulta.

Isenção de responsabilidade: não sou nenhum especialista em DB e estou usando PARTITION BY para minha cláusula, portanto, pode não ser exatamente o mesmo resultado para esta consulta. Para mim, a coluna em questão é um pedido pago do cliente, então o valor geralmente não muda depois de definido.

Além disso, certifique-se de ter índices, especialmente se tiver uma cláusula WHERE na instrução SELECT. Um índice filtrado funcionou muito bem para mim, pois estava filtrando com base nos status de pagamento.


Minha consulta usando PARTITION por

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

A parte 'IS NOT NULL' não é necessária se a coluna não puder ser anulada.


Quando digo que o aumento de desempenho foi enorme, quero dizer que foi essencialmente instantâneo ao atualizar um pequeno número de linhas. Com os índices corretos, consegui obter uma atualização que demorou o mesmo tempo que a consulta "interna" sozinha:

  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
Simon_Weaver
fonte
@AleksandrFedorenko para ser honesto, fiquei surpreso que funcionou, mas feliz que funcionou :) os índices que eu criei também foram importantes
Simon_Weaver
1
Obrigado por esta dica útil. Cumprimentos.
Sedat Kumcu
3

Eu fiz isso para a minha situação e trabalhei

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
Arun Prasad ES
fonte
1

Maneira simples e fácil de atualizar o cursor

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
Rahim Surani
fonte
1

Se a tabela não tiver relação, apenas copie tudo na nova tabela com o número da linha e remova a antiga e renomeie a nova com a antiga.

Select   RowNum = ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) , * INTO cdm.dbo.SALES2018 from 
(
select * from SALE2018) as SalesSource
Ubaid Mughal
fonte