Обновление SQL с помощью row_number ()

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?

user609511
источник
Опубликуйте образцы данных и ожидаемые результаты, это лучший способ получить ответ SQL. Иначе ваш? не имеет смысла и даст такие ответыUPDATE myCol = myCol+1 FROM MyTable WHERE ID=@MyID
JonH

Ответы:

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
вы можете значительно улучшить производительность будущих обновлений с помощью предложения WHERE (см. мой ответ, основанный на этом)
Simon_Weaver
72
DECLARE @id INT 
SET @id = 0 
UPDATE DESTINATAIRE_TEMP
SET @id = CODE_DEST = @id + 1 
GO 

попробуй это

http://www.mssqltips.com/sqlservertip/1467/populate-a-sql-server-column-with-a-sequential-number-not-using-an-identity/

vasin1987
источник
2
Только одно из вышеперечисленных сработало для меня в SQL Server 2012
Аня Хоуп
11
Хороший ответ! Попробуйте: SET CODE_DEST = @id, @id = @id + 1для удобочитаемости.
Р. Остерхолт
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
источник
3
Это работает, только если значения в столбце RS_NOM уникальны, не так ли? Сомневаюсь, что это можно предположить.
Тоби
17

Вторая попытка не удалась в первую очередь потому, что вы назвали 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 заставляет его просто пропускать строки, где значение не изменилось. Должен сказать, я был поражен тем, насколько быстро он мог выполнить мой запрос.

Отказ от ответственности: я не эксперт по БД, и я использую PARTITION BY для своего предложения, поэтому для этого запроса могут быть не совсем те же результаты. Для меня рассматриваемый столбец является оплаченным заказом клиента, поэтому значение обычно не меняется после его установки.

Также убедитесь, что у вас есть индексы, особенно если у вас есть предложение WHERE в инструкции SELECT. У меня отлично сработал отфильтрованный индекс, поскольку я выполнял фильтрацию по статусам платежей.


Мой запрос с использованием 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 не требуется, если столбец не допускает значения 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
Simon_Weaver
источник
@AleksandrFedorenko, если честно, я был удивлен, что это сработало, но рад, что это так :) были важны индексы, которые я создал
Simon_Weaver
1
Спасибо за этот полезный совет. С уважением.
Седат Кумджу,
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
Арун Прасад ES
источник
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
Убайд Могол
источник