代码之家  ›  专栏  ›  技术社区  ›  esac

如何在SQL中保持滚动校验和?

  •  3
  • esac  · 技术社区  · 14 年前

    我试图保持一个滚动校验和,以说明顺序,所以采取以前的'校验和'和异或它与当前的一个,并生成一个新的校验和。

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

    我怎样才能完成这样的事情?

    (请注意,这些计算完全是虚构的,仅供说明)

    3 回复  |  直到 14 年前
        1
  •  2
  •   Martin Smith    14 年前

    这基本上就是 running total 问题。

    编辑:

    我最初的主张是,这是为数不多的基于光标的解决方案实际上表现最好的地方之一。三角形自联接解决方案的问题是,它将重复地重新计算与下一步的子计算相同的累积校验和,因此,由于所需的工作随行数呈指数增长,因此伸缩性不强。

    科琳娜的回答采用了“古怪更新”的方法。我已经调整它来做校验和,在我的测试中发现光标解决方案用了3秒而不是26秒。两者产生了相同的结果。然而不幸的是,它依赖于更新行为的一个未记录的方面。我肯定会读这个讨论 here 在决定是否在生产代码中依赖它之前。

    还有第三种可能性 here the discussion here 这似乎是一个很好的可能性,计算运行总类型的东西在显示时间,但由光标执行时,计算结果必须保存回来。

    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
    

    Jeff Moden 文章

    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
    
        2
  •  1
  •   Corina    14 年前

    我不确定滚动校验和,但对于滚动校验和,您可以使用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
    
        3
  •  1
  •   Thomas    14 年前
    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
    

    要滚动任何东西,你需要一个命令行的外观。可以是名字,一个整数键,一个日期等等。在我的示例中,我使用了name(即使示例数据中的顺序不是字母顺序)。另外,我正在使用 Checksum_Agg

    另外,您最好有一个惟一的值来比较内部查询和外部查询。例如。, Where T1.PK < T.PK Name 如果有一个独特的约束,它也可以很好地工作。