Как сохранить скользящую контрольную сумму в SQL? - PullRequest
3 голосов
/ 16 июля 2010

Я пытаюсь сохранить скользящую контрольную сумму для учета заказа, поэтому возьмите предыдущую «контрольную сумму» и присвойте ей текущую и сгенерируйте новую контрольную сумму.

Name      Checksum     Rolling Checksum
------    -----------  -----------------
foo       11829231     11829231
bar       27380135     checksum(27380135 ^ 11829231) = 93291803
baz       96326587     checksum(96326587 ^ 93291803) = 67361090

Как бы я мог сделать что-то подобное?

(Обратите внимание, что расчеты полностью выполнены и приведены только для иллюстрации)

Ответы [ 3 ]

2 голосов
/ 16 июля 2010

Это в основном проблема промежуточного .

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
1 голос
/ 16 июля 2010

Я не уверен насчет скользящей контрольной суммы, но для скользящей суммы, например, вы можете сделать это с помощью команды UPDATE:

declare @a table (name varchar(2), value int, rollingvalue int)
insert into @a
    select 'a', 1, 0 union all select 'b', 2, 0 union all select 'c', 3, 0 

select * from @a

declare @sum int
set @sum = 0

update @a
set @sum =  rollingvalue = value + @sum 

select * from @a
1 голос
/ 16 июля 2010
Select Name, Checksum
    , (Select T1.Checksum_Agg(Checksum)
        From Table As T1
        Where T1.Name < T.Name) As RollingChecksum
From Table As T
Order By T.Name

Чтобы сделать что-либо, вам нужно некоторое подобие порядка в строках.Это может быть имя, целочисленный ключ, дата или что-то еще.В моем примере я использовал имя (хотя порядок в данных примера не в алфавитном порядке).Кроме того, я использую функцию Checksum_Agg в SQL.

Кроме того, в идеале у вас должно быть уникальное значение, по которому вы сравниваете внутренний и внешний запрос.Например, Where T1.PK < T.PK для целочисленного или даже строкового ключа будет работать хорошо.В моем решении, если бы Name имело уникальное ограничение, оно также работало бы достаточно хорошо.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...