Aktualizacja SQL z row_number ()

113

Chcę zaktualizować moją kolumnę CODE_DEST o kolejny numer. Mam:

CODE_DEST   RS_NOM
null        qsdf
null        sdfqsdfqsdf
null        qsdfqsdf

Chciałbym go zaktualizować, aby był:

CODE_DEST   RS_NOM
1           qsdf
2           sdfqsdfqsdf
3           qsdfqsdf

Wypróbowałem ten kod:

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

To nie działa, ponieważ )

Próbowałem też:

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

Ale to również nie działa z powodu związku.

Jak zaktualizować kolumnę za pomocą ROW_NUMBER()funkcji w programie SQL Server 2008 R2?

user609511
źródło
Opublikuj przykładowe dane i oczekiwane wyniki, to najlepszy sposób na uzyskanie odpowiedzi SQL za pomocą. W przeciwnym razie twój? nie ma sensu i przyniesie takie odpowiedziUPDATE myCol = myCol+1 FROM MyTable WHERE ID=@MyID
JonH

Odpowiedzi:

189

Jeszcze jedna opcja

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
źródło
Czy to genialna odpowiedź, czy też wspaniale jest, że SQL Server może aktualizować się w zagnieżdżonym SELECT? Myślę, że obaj są…
Bigjim,
możesz znacznie poprawić wydajność przyszłych aktualizacji dzięki klauzuli WHERE (zobacz moją odpowiedź na tej podstawie)
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
źródło
3
Działa to tylko wtedy, gdy wartości w kolumnie RS_NOM są unikalne, prawda? Wątpię, czy można to założyć.
Toby,
17

Twoja druga próba nie powiodła się głównie dlatego, że nazwałeś CTE taką samą jak podstawowa tabela i sprawiłeś, że CTE wyglądało tak, jakby było rekurencyjnym CTE , ponieważ zasadniczo odwoływało się do siebie. Rekurencyjne CTE musi mieć specyficzną strukturę, która wymaga używaniaUNION ALL operatora zadanej.

Zamiast tego możesz po prostu nadać CTE inną nazwę, a także dodać do niej kolumnę docelową:

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
źródło
16

To jest zmodyfikowana wersja odpowiedzi @Aleksandr Fedorenko dodająca klauzulę 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

Dodając klauzulę WHERE zauważyłem, że wydajność znacznie się poprawiła w przypadku kolejnych aktualizacji. Wydaje się, że serwer Sql aktualizuje wiersz, nawet jeśli wartość już istnieje i zajmuje to trochę czasu, więc dodanie klauzuli where powoduje, że po prostu pomija wiersze, w których wartość nie uległa zmianie. Muszę powiedzieć, że byłem zdumiony, jak szybko może wykonać moje zapytanie.

Zastrzeżenie: nie jestem ekspertem od DB i używam PARTITION BY dla mojej klauzuli, więc może to nie być dokładnie takie same wyniki dla tego zapytania. Dla mnie ta kolumna to płatne zamówienie klienta, więc wartość generalnie nie zmienia się po jej ustawieniu.

Upewnij się również, że masz indeksy, zwłaszcza jeśli masz klauzulę WHERE w instrukcji SELECT. Filtrowany indeks działał świetnie, ponieważ filtrowałem na podstawie statusów płatności.


Moje zapytanie przy użyciu PARTITION wg

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

Część „NIE JEST NULL” nie jest wymagana, jeśli kolumna nie dopuszcza wartości null.


Kiedy mówię, że wzrost wydajności był ogromny, mam na myśli to, że był on zasadniczo natychmiastowy podczas aktualizowania niewielkiej liczby wierszy. Dzięki odpowiednim indeksom udało mi się uzyskać aktualizację, która zajęła tyle samo czasu, co samo zapytanie „wewnętrzne”:

  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
źródło
@AleksandrFedorenko szczerze mówiąc byłem zaskoczony, że zadziałało, ale cieszę się, że tak się stało :) indeksy były ważne, które też utworzyłem
Simon_Weaver
1
Dzięki za tę przydatną wskazówkę. Z poważaniem.
Sedat Kumcu
3

Zrobiłem to dla mojej sytuacji i pracowałem

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
źródło
1

Prosty i łatwy sposób na aktualizację kursora

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
źródło
1

Jeśli tabela nie ma relacji, po prostu skopiuj wszystko do nowej tabeli z numerem wiersza i usuń starą i zmień nazwę nowej na starą.

Select   RowNum = ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) , * INTO cdm.dbo.SALES2018 from 
(
select * from SALE2018) as SalesSource
Ubaid Mogol
źródło