Это в основном проблема промежуточного .
Edit:
Моим первоначальным утверждением было то, что это одно из немногих мест, где решение на основе курсора действительно работает лучше всего. Проблема с решением треугольного самосоединения состоит в том, что он будет многократно пересчитывать ту же кумулятивную контрольную сумму, что и подрасчет для следующего шага, поэтому он не очень масштабируемый, так как требуемая работа растет экспоненциально с количеством строк.
В ответе Корины используется подход «причудливое обновление». Я настроил его для выполнения контрольной суммы и в своем тесте обнаружил, что для решения курсора потребовалось 3 секунды, а не 26 секунд. Оба дали одинаковые результаты. К сожалению, однако, он опирается на недокументированный аспект поведения обновления. Я определенно прочитал бы обсуждение здесь , прежде чем решить, положиться ли на это в рабочем коде.
Здесь описана третья возможность (с использованием CLR), которую я не успел проверить. Но из обсуждения здесь представляется хорошей возможностью для вычисления промежуточных сумм типа во время отображения, но без выполнения курсором, когда результат вычисления должен быть сохранен обратно.
CREATE TABLE TestTable
(
PK int identity(1,1) primary key clustered,
[Name] varchar(50),
[CheckSum] AS CHECKSUM([Name]),
RollingCheckSum1 int NULL,
RollingCheckSum2 int NULL
)
/*Insert some random records (753,571 on my machine)*/
INSERT INTO TestTable ([Name])
SELECT newid() FROM sys.objects s1, sys.objects s2, sys.objects s3
Первый подход: на основе Джеффа Модена Статья
DECLARE @RCS int
UPDATE TestTable
SET @RCS = RollingCheckSum1 =
CASE WHEN @RCS IS NULL THEN
[CheckSum]
ELSE
CHECKSUM([CheckSum] ^ @RCS)
END
FROM TestTable WITH (TABLOCKX)
OPTION (MAXDOP 1)
Второй подход - Использование тех же опций курсора, что и у Хьюго Корнелиса в обсуждении этой статьи.
SET NOCOUNT ON
BEGIN TRAN
DECLARE @RCS2 INT
DECLARE @PK INT, @CheckSum INT
DECLARE curRollingCheckSum CURSOR LOCAL STATIC READ_ONLY
FOR
SELECT PK, [CheckSum]
FROM TestTable
ORDER BY PK
OPEN curRollingCheckSum
FETCH NEXT FROM curRollingCheckSum
INTO @PK, @CheckSum
WHILE @@FETCH_STATUS = 0
BEGIN
SET @RCS2 = CASE WHEN @RCS2 IS NULL THEN @CheckSum ELSE CHECKSUM(@CheckSum ^ @RCS2) END
UPDATE dbo.TestTable
SET RollingCheckSum2 = @RCS2
WHERE @PK = PK
FETCH NEXT FROM curRollingCheckSum
INTO @PK, @CheckSum
END
COMMIT
Тест они одинаковы
SELECT * FROM TestTable
WHERE RollingCheckSum1<> RollingCheckSum2