Technical Article

Compare SQL code of 2 objects.

,

Scripst are written to use database with name DBAdmin for maintanace. You can use any existing databsae, but you need to change all scripts to use other name then DBAdmin.

You need create table dba_objScript and procedure usp_DBA_objScript_compare in DBAdmin

If you need compare objects across servers, you need to have other servers added as linked server on server where you run DBAdmin..usp_DBA_objScript_compare is located.

EACH server need to have database DBAdmin and table dba_objScript.

EACH DATABASE, from which you want compare objects need to have stored proc usp_dba_objScript_save.

Only used in SQL2000.

/*********************************************************
** Name            : usp_DBA_objScript_compare
** Created By    : (LLT)
** Desc            : compares 2 DB objects (Sp, function,triggesr). Ignores comments and spaces/empty lines. Objects can be in diferent servers, DBs, and/or owner
** depend on    : procedure usp_dba_objScript_save need to be added to each Database you need to compare scripts from. Code is below 
** depend on    : table dba_objScript (compareDt datetime,server varchar(128), dbName varchar(128), objName varchar(128), lineNum int, lineText varchar(4000),lineID int identity)
**
**        (c)All rights reserved, S_M_Y_T_H Solutions LLC
*******************************************************************************
REQUIREMENTS:
Scripst are written to use database with name DBAdmin for maintanace.
You need create table dba_objScript and procedure usp_DBA_objScript_compare there
---------------
use dbadmin
CREATE TABLE [dba_objScript] (
    [compareDt] [datetime] NULL ,
    [server] [varchar] (128) ,
    [dbName] [varchar] (128) ,
    [owner] [varchar] (128) ,
    [objName] [varchar] (128) ,
    [lineNum] [int] NULL ,
    [lineText] [varchar] (4000) ,
    [lineID] [int] IDENTITY (1, 1) NOT NULL 
) 

If you need compare objects across servers, you need to have other servers added as linked server on server where 
you run DBAdmin..usp_DBA_objScript_compare is located

EACH DATABASE, from which you want compare objects need to have stored proc usp_dba_objScript_save. Code is below.
*******************************************************************************
**        Sample run.
exec dbadmin.dbo.usp_DBA_objScript_compare @objName1='proc_SND_customerProfile_save',@objName2='proc_SND_customerProfile_save150'
    ,@dbname1='smyth',@dbname2='tarnell',@server1='production-sql',@server2='developement-sql'

it will Compares objects
[production-sql].smyth.dbo.proc_SND_customerProfile_save 
and    
[developement-sql].tarnell.dbo.proc_SND_customerProfile_save150

********************************************************************************/
--drop proc usp_DBA_objScript_compare
create proc usp_DBA_objScript_compare
--declare 
    @objName1 varchar(128),@objName2 varchar(128)=null -- object names to compare, use the sa
    ,@server1 varchar(128)=null, @server2 varchar(128)=null
    ,@dbname1 varchar(128)=null, @dbname2 varchar(128)=null
    ,@owner1 varchar(128)=null, @owner2 varchar(128)=null
as

set nocount on
--set ansi_warnings off
declare @sql nvarchar(4000), @scriptDt datetime
/*
select
    @server1='bsands-sql', @server2='bsands-web'
    ,@owner1='dbo', @owner2='dbo'
    ,@dbname1='sandsProd', @dbname2='sandsProd'
    ,@objName1='proc_SND_RatingHistory_add',    @objName2='proc_SND_RatingHistory_add'
*/set @scriptDt = dateadd(mi,-5,getdate())
if isnull(@server1,'') = ''        set @server1    = @@servername
if isnull(@dbname1,'') = ''        set @dbname1    = db_name()
if isnull(@owner1,'') = ''        set @owner1        = user

if isnull(@server2,'') = ''        set @server2    = @server1
if isnull(@owner2,'') = ''        set @owner2        = @owner1
if isnull(@dbname2,'') = ''        set @dbname2    = @dbname1
if isnull(@objName2,'') = ''    set @objName2    = @objName1


declare @srvName1 varchar(128), @srvName2 varchar(128)
declare @id1 varchar(200),@id2 varchar(200)
set @id1=@server1+'.'+@dbname1+'.'+@owner1+'.'+@objName1
set @id2=@server2+'.'+@dbname2+'.'+@owner2+'.'+@objName2
-- script obj1 in its server
select @sql = 
    'exec ['+@server1+'].['+@dbname1+'].['+@owner1+'].usp_dba_objScript_save @objName='''+@objName1+''',@owner='''+@owner1+''''
print @sql
exec sp_executesql @sql

-- script obj2 in its server
select @sql = 
    'exec ['+@server2+'].['+@dbname2+'].['+@owner2+'].usp_dba_objScript_save @objName='''+@objName2+''',@owner='''+@owner2+''''
print @sql
exec sp_executesql @sql

declare @ctStart int, @clStart int, @ctEnd int, @clEnd int
declare @cId int

create table #s1 ( lineNum int, lineText varchar(7500), lineCS bigint,lineID int identity)
set @sql ='insert into #s1(lineText) select ltrim(left(lineText,7500)) from ['
    +@server1+'].dbadmin.dbo.dba_objScript '
    +'where server='''+isnull(@srvName1,@server1)
    +''' and owner='''+@owner1+''' and dbname='''+@dbname1+''' and objName='''+@objName1+''' order by lineID'
--print @sql
exec sp_executesql @sql

create table #s2 ( lineNum int, lineText varchar(7500), lineCS bigint,lineID int identity)
set @sql ='insert into #s2(lineText) select ltrim(left(lineText,7500)) from ['
    +@server2+'].dbadmin.dbo.dba_objScript '
    +'where server='''+isnull(@srvName2,@server2)
    +''' and owner='''+@owner2+''' and dbname='''+@dbname2+''' and objName='''+@objName2+''' order by lineID'
--print @sql
exec sp_executesql @sql


update #s1 set lineText= dbcommon.dbo.fVal_trimSpaces(lineText)
update #s2 set lineText= dbcommon.dbo.fVal_trimSpaces(lineText)

-- delete one line comments
delete from #s1 where lineText like '--%' 
delete from #s1 where lineText like '/*%*/' 
delete from #s1 where len(lineText)=0 
declare @rcnt int
set @rcnt=1
while (@rcnt>0)
begin
    update #s1 set lineText = substring(lineText,1,charindex('/*', lineText)-1) 
    + substring(lineText,charindex('*/', lineText,charindex('/*', lineText))+2, len(lineText))
    where charindex('/*', lineText)>0 and charindex('*/', lineText,charindex('/*', lineText)) >0 
    and charindex('/*', lineText)< charindex('*/', lineText,charindex('/*', lineText))
    set @rcnt=@@rowcount
end
-- delete multi-line comments

select @ctStart =0, @clStart =1, @ctEnd =0, @clEnd =0
while (@clStart>0)
begin
    set @clStart=0
    -- find start of the comment
    select top 1 @clStart=lineID,@ctStart=charindex('/*', lineText)-1 from #s1 
        where charindex('/*', lineText)>0 and lineID> @clEnd
        order by lineId
    if @@rowcount=0
        break
    -- find end of the comment
    if (@clStart > 0)
    begin
        select @clEnd = 0, @ctEnd=0
        select top 1 @clEnd=lineID,@ctEnd= charindex('*/', lineText)+2 from #s1 
        where charindex('*/', lineText)>0 and lineID> @clStart 
        order by lineId
    end
    if (@clEnd > 0) begin
        update #s1 set lineText=substring(lineText, 1, @ctStart )+'/*...' where lineID=@clStart
        delete from #s1 where lineID>@clStart and lineID < @clEnd
        update #s1 set lineText= '...*/'+substring(lineText, @ctEnd, len(lineText)-@ctEnd+5) where lineID=@clEnd
    end
    else
        break
    --print str(@clStart)+str(@clEnd)+str(@ctStart)+str(@ctEnd)
end

-- renumber
set @cId =1
update #s1 set lineNum=@cid, @cid=@cid+1

------------- s2

-- delete one line comments
delete from #s2 where lineText like '--%' 
delete from #s2 where lineText like '/*%*/' 
delete from #s2 where len(lineText)=0 
set @rcnt=1
while (@rcnt>0)
begin
    update #s2 set lineText = substring(lineText,1,charindex('/*', lineText)-1) 
    + substring(lineText,charindex('*/', lineText,charindex('/*', lineText))+2, len(lineText))
    where charindex('/*', lineText)>0 and charindex('*/', lineText,charindex('/*', lineText)) >0 
    and charindex('/*', lineText)< charindex('*/', lineText,charindex('/*', lineText))
    set @rcnt=@@rowcount
end

-- delete multi-line comments

select @ctStart =0, @clStart =1, @ctEnd =0, @clEnd =0
while (@clStart>0)
begin
    set @clStart=0
    -- find start of the comment
    select top 1 @clStart=lineID,@ctStart=charindex('/*', lineText)-1 from #s2 
        where charindex('/*', lineText)>0 and lineID> @clEnd
        order by lineId
    if @@rowcount=0
        break
    -- find end of the comment
    if (@clStart > 0)
    begin
        select @clEnd = 0, @ctEnd=0
        select top 1 @clEnd=lineID,@ctEnd= charindex('*/', lineText)+2 from #s2
        where charindex('*/', lineText)>0 and lineID> @clStart 
        order by lineId
    end
    if (@clEnd > 0) begin
        update #s2 set lineText=substring(lineText, 1, @ctStart )+'/*...' where lineID=@clStart
        delete from #s2 where lineID>@clStart and lineID < @clEnd
        update #s2 set lineText= '...*/'+substring(lineText, @ctEnd, len(lineText)-@ctEnd+5) where lineID=@clEnd
    end
    else
        break
    --print str(@clStart)+str(@clEnd)+str(@ctStart)+str(@ctEnd)
end

-- renumber
set @cId =1
update #s2 set lineNum=@cid, @cid=@cid+1

----- end s2 ------
delete from #s1 where len(ltrim(rtrim(lineText)))=0 
delete from #s2 where len(ltrim(rtrim(lineText)))=0 

--Now compare only lineCS
update #s1 set lineCS=checksum(replace(lineText,' ',''))
update #s2 set lineCS=checksum(replace(lineText,' ',''))

-- search were first identical line after comments
declare @SC1 int, @SC2 int
select @SC1=0, @SC2=0
select top 1 @SC1=s1.lineNum,@SC2=s2.lineNum from #s1 s1 join #s2 s2 on s1.lineCS=s2.lineCS where s1.lineText not in ('/*...','...*/')
    order by s1.lineNum, s2.lineNum 

create table #tblRet (idd int, files varchar(80),lineId1 int, object1 varchar(2000),lineId2 int, object2 varchar(2000),ordr int)
    select @sql=
    'insert into #tblRet select 1 idd,''<--->'',0 lineid1, '''+@id1+''', 0 lineid2, '''+@id2+''' [object2],-6'
    exec sp_executesql @sql

    insert into #tblRet (idd, files,object1,object2, ordr) values(1,'','Different lines','',-5)
    insert into #tblRet (idd, files,object1,object2, ordr) values(1,'','Identical Lines','',-4)
if (@SC1>1 or @SC2 >1 )
begin
-- select code that different at the beginng into results
    insert into #tblRet (idd, files,object1,object2, ordr) values(1,'----','---Difference at the beginning ---','-----------',-3)
    select @sql=
    'insert into #tblRet select 2 idd,''<---'',lineID , linetext [object1], 0, '' '' [object2],-2'
    +' from #s1 where lineNum<'+str(@SC1)+' order by lineId'
    exec sp_executesql @sql

    select @sql=
    'insert into #tblRet select 3 idd,''--->'',null,null,lineID , linetext [object2],-1'
    +' from #s2 where lineNum<'+str(@SC2)+' order by lineId'

--    print '1.'+@sql
    exec sp_executesql @sql

    delete from #s1 where lineNum < @SC1
    delete from #s2 where lineNum < @SC2
end

-- code that different at the begining

-- renumber
set @cId =1
update #s1 set lineNum=@cid, @cid=@cid+1
set @cId =1
update #s2 set lineNum=@cid, @cid=@cid+1


-- mid/end code difference
declare @ls1 int, @ls2 int
select @ls1=max(lineNUm) from #s1
select @ls2=max(lineNUm) from #s2
--select @ls1,@ls2
-- where for mid code difference
declare @where varchar(1000), @cnt int, @order varchar(100), @lDiff varchar(20)
    , @sign1 varchar(10), @sign2 varchar(10), @step int
--select @ls1=12, @ls2=10
select @sign1=case when @ls1-@ls2>0 then '=' else '+' end
select @sign2=case when @ls1-@ls2>0 then '+' else '=' end

select @where ='s1.lineNum=s2.lineNum ', @cnt=@ls1-@ls2
while (abs(@cnt) >0) begin
    set @step= abs((@ls1-@ls2)-@cnt)+1
    set @lDiff= @sign1+ltrim(str(@step))+@sign2
    select @where = @where +' or s1.lineNum'+@lDiff+'s2.lineNum ', @cnt=sign(@cnt)*(abs(@cnt)-1)
end

--select @where, @order
--select * from #s1
--select * from #s2

--print 'compare '+@id1 +' and '+@id2

    insert into #tblRet (idd, files,object1,object2, ordr) values(3,'-----','----Identical code starts----','',0)
    
    select @sql=
+char(10)+'insert into #tblRet select 4 idd,case when s2.lineID is null then ''<--'' when s1.lineID is null then ''-->'' else ''match'' end'
+char(10)+',s1.lineID, isnull(s1.linetext,'''') [server1], s2.lineID, isnull(s2.linetext,'''') [server2],isnull(s1.lineNum, s2.lineNUm) idd2'
+char(10)+' from #s1 s1    full join #s2 s2 on s1.lineCS=s2.lineCS'
+char(10)+' and ('+@where+')'
+char(10)+' order by idd,idd2 '

print @sql
exec sp_executesql @sql

select @SC1=count(*) from #tblRet where files='<--'
select @SC2=count(*) from #tblRet where files='-->'
select @cId=count(*) from #tblRet where files in ('match')
select @ls1=count(*) from #tblRet where files in ('<--','-->')

update #tblRet set lineId1=@SC1,lineId2=@SC2 where files='<---->' 
update #tblRet set lineId1 = @ls1 where object1='Different lines' 
update #tblRet set lineId1 = @cId where object1='Identical lines' 


select * from #tblRet order by idd, ordr

set nocount off


GO
SET QUOTED_IDENTIFIER OFF 
GO
SET ANSI_NULLS ON 
GO

----------------------------------
SET QUOTED_IDENTIFIER ON 
GO
SET ANSI_NULLS ON 
GO



/*************************************
** saves object code in DB
Runs in current DB ONLY
*************************************/create proc usp_DBA_objScript_save
    @objName varchar(128)
    ,@owner varchar(128) = 'dbo'
as
begin
set nocount on
declare     @dbname varchar(128)    ,@server varchar(128)    , @fullObjName varchar(512)


    select @dbName=db_name()
            ,@server = @@servername
            --,@objName = 'proc_SND_reportPrice_save'
    
    select @fullObjName = @dbname+'.'+@owner+'.'+@objName
    --select @fullObjName
        delete from dbadmin.dbo.dba_objScript where server=@server and dbName=@dbname and objName=@objName and owner=@owner
        insert into dbadmin.dbo.dba_objScript (lineText)
            EXEC sp_helptext @fullObjName

        update dbadmin.dbo.dba_objScript set 
            server=@server, dbName=@dbname, owner=@owner, objName=@objName
            ,compareDt=getdate()
            where compareDt is null and objName is null


set nocount off
end


GO
SET QUOTED_IDENTIFIER OFF 
GO
SET ANSI_NULLS ON 
GO

Rate

You rated this post out of 5. Change rating

Share

Share

Rate

You rated this post out of 5. Change rating