Mise à jour SQL avec row_number ()

113

Je souhaite mettre à jour ma colonne CODE_DEST avec un numéro incrémentiel. J'ai:

CODE_DEST   RS_NOM
null        qsdf
null        sdfqsdfqsdf
null        qsdfqsdf

Je voudrais le mettre à jour pour qu'il soit:

CODE_DEST   RS_NOM
1           qsdf
2           sdfqsdfqsdf
3           qsdfqsdf

J'ai essayé ce code:

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

Cela ne fonctionne pas à cause du )

J'ai également essayé:

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

Mais cela ne fonctionne pas non plus à cause de l'union.

Comment puis-je mettre à jour une colonne à l'aide de la ROW_NUMBER()fonction dans SQL Server 2008 R2?

user609511
la source
Publiez des exemples de données et des résultats attendus, c'est la meilleure façon d'obtenir une réponse SQL avec. Sinon votre? n'a aucun sens et donnera des réponses comme celle-ciUPDATE myCol = myCol+1 FROM MyTable WHERE ID=@MyID
JonH

Réponses:

189

Une autre option

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
la source
Est-ce une réponse brillante ou est-ce génial que SQL Server puisse se mettre à jour dans un SELECT imbriqué? Je pense que les deux sont ...
Bigjim
vous pouvez considérablement améliorer les performances de la mise à jour future avec une clause WHERE (voir ma réponse basée sur ceci)
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
la source
3
Cela ne fonctionne que si les valeurs de la colonne RS_NOM sont uniques, n'est-ce pas? Je doute que cela puisse être supposé.
Toby le
17

Votre deuxième tentative a échoué principalement parce que vous avez nommé le CTE de la même manière que la table sous-jacente et que le CTE a l'air d'être un CTE récursif , car il s'est essentiellement référé à lui-même. Un CTE récursif doit avoir une structure spécifique qui nécessite l'utilisation de l' UNION ALLopérateur set.

Au lieu de cela, vous auriez pu simplement donner un nom différent au CTE et y ajouter la colonne cible:

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
la source
16

Ceci est une version modifiée de la réponse de @Aleksandr Fedorenko ajoutant une clause 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

En ajoutant une clause WHERE, j'ai trouvé que les performances se sont considérablement améliorées pour les mises à jour ultérieures. Sql Server semble mettre à jour la ligne même si la valeur existe déjà et que cela prend du temps, donc l'ajout de la clause where fait simplement sauter les lignes où la valeur n'a pas changé. Je dois dire que j'ai été étonné de la vitesse à laquelle il pouvait exécuter ma requête.

Clause de non-responsabilité: je ne suis pas un expert de la base de données et j'utilise PARTITION BY pour ma clause, il se peut donc que les résultats ne soient pas exactement les mêmes pour cette requête. Pour moi, la colonne en question est la commande payée d'un client, donc la valeur ne change généralement pas une fois qu'elle est définie.

Assurez-vous également que vous avez des index, en particulier si vous avez une clause WHERE sur l'instruction SELECT. Un index filtré fonctionnait très bien pour moi car je filtrais en fonction des statuts de paiement.


Ma requête utilisant PARTITION par

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

La partie 'IS NOT NULL' n'est pas requise si la colonne n'est pas NULL.


Quand je dis que l'augmentation des performances était massive, je veux dire qu'elle était essentiellement instantanée lors de la mise à jour d'un petit nombre de lignes. Avec les bons index, j'ai pu réaliser une mise à jour qui a pris le même temps que la requête `` interne '' elle-même:

  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
la source
@AleksandrFedorenko pour être honnête, j'ai été surpris que cela ait fonctionné mais heureux que cela ait fonctionné :) les index étaient importants que j'ai créés aussi
Simon_Weaver
1
Merci pour cette astuce utile. Meilleures salutations.
Sedat Kumcu le
3

J'ai fait ça pour ma situation et j'ai travaillé

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
la source
1

Un moyen simple et facile de mettre à jour le curseur

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
la source
1

Si la table n'a pas de relation, copiez simplement tout dans la nouvelle table avec le numéro de ligne et supprimez l'ancienne et renommez la nouvelle avec l'ancienne.

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