Skip to Content

Sometimes it is necessary to make sure that content of two tables are the same or to monitor changes to the table.

For example for MS SQL it is possible to do that with CHECKSUM_AGG function


SELECT CHECKSUM_AGG(BINARY_CHECKSUM(*)) FROM table

As far as I know there is no similar aggregation function for Sybase ASE, but some close functionality can be achieved with XOR + update trick


create procedure sp__hashcode
 @objectname varchar(255)
,@excludelist varchar(16384) = null
,@includelist varchar(16384) = null
,@whereclause varchar(16384) = null
,@hashalgorithm varchar(20) = 'MD5'
,@using varchar(255) = null
,@hash bigint = 0 output
,@debug bit = 1
as
begin
    declare @objectid int
           ,@columnlist varchar(16384)
           ,@rowcount int
    select @objectid = object_id(@objectname), @hash = isnull(@hash,0)
    if @objectid is null begin
        /* 17461, "Object does not exist in this database." */
        raiserror 17461
        return (1)
    end
    /* Create temporary table for column list */
    create table #columnlist (
        name varchar(255)
    )
    insert into #columnlist
    select distinct c.name
    from syscolumns c
    join systypes t on (c.type = t.type)
    where c.id = @objectid
    and t.name not in ('image','text','unitext') -- ignore LOBs
    and (isnull(c.xstatus,0) & 1) != 1 -- ignore off row
    /* Delete columns which exists in exclude list */
    if @excludelist is not null begin
        set @excludelist = str_replace(@excludelist, '''', '''''')
        set @excludelist = '''' + str_replace(@excludelist, ',', ''',''') + ''''
        if @debug = 1
            print 'Exclude list: %1!', @excludelist
        execute ('delete from #columnlist where name in (' + @excludelist + ')')
    end
    /* Delete columns which not exists in include list */
    if @includelist is not null begin
        set @includelist = str_replace(@includelist, '''', '''''')
        set @includelist = '''' + str_replace(@includelist, ',', ''',''') + ''''
        if @debug = 1
            print 'Include list: %1!', @includelist
        execute ('delete from #columnlist where name not in (' + @includelist + ')')
    end
    /* Put columns into the comma separated list */
    update #columnlist
    set @columnlist = @columnlist + case when @columnlist is null then null else ',' end + name
    if @debug = 1
        print 'Column list: %1!', @columnlist
    if @columnlist is not null
        if @using is null
            execute ( 'update ' + @objectname + ' set @hash=@hash^convert(bigint,hashbytes(''' + @hashalgorithm + ''',' + @columnlist + ')) ' + @whereclause )
        else
            execute ( 'update ' + @objectname + ' set @hash=@hash^convert(bigint,hashbytes(''' + @hashalgorithm + ''',' + @columnlist + ',using ' + @using + ')) ' + @whereclause )
    select @rowcount = @@rowcount
    if @debug = 1 begin
        print 'Rows processed: %1!', @rowcount
        print 'Hash: %1!', @hash
    end
    return (0)
end
go

Example of usage:


sp__hashcode sysobjects, @includelist='name,id,uid,type', @whereclause='where id < 20' 

Definitely, there is no one hundred percent guarantee that two different tables will return distinct hashes, at least because hashbytes calculates hash based on the underlying bytes, and in case of different data types with same bytes it returns the same value. This query demonstrates it


select hashbytes ('MD5', cast(49 as tinyint)), hashbytes ('MD5', '1') 

Also it is quite heavy, and have other limitations (like lengths of column names) but still useful for quick comparation of tables and watching for changes for relevantly small tables.

p.s. criticism is welcome

To report this post you need to login first.

Be the first to leave a comment

You must be Logged on to comment or reply to a post.

Leave a Reply