[Case cleanup]
Today, I received a call from a customer and reported that I have used Tiger Extreme Software for many years.
Due to the slow speed of the software card, I want to reduce the size of the database by cleaning up historical data.
Improve database performance, so care.
Inadvertently mishandled, deleted all the basic information such as the classification of the product information supplier and the stored value of the membership card.
【problem analysis】
In such special circumstances, you need to stop the SQL service historically, and copy the physical files of D:\Program Files\Microsoft SQL Server\MSSQL\Data
MDF and LDF database for backup.
Stop all writing and updating operations to the database, and don't make the situation worse and cause new error coverage.
[Solution experience]
1. Of course, the first thing is to back up the data in time, and you can play it whatever you want.
2. Obtain basic information of database logs through fn_dblog(NULL, NULL) and DBCC LOG provided by Microsoft,
But they are all binary codes, so I don't understand them. Recently successfully parsed SQL SERVER LOG information
In the output result of fn_dblog(NULL,NULL),
Get the table name is the AllocUnitName field.
Specific method of obtaining: AllocUnitName like'dbo.TEST%'
The operation type is: Operation
The data is: [RowLog Contents 0] field content
If it is an UPDATE operation: the modified data is stored in the [RowLog Contents 1] field
The most basic 3 types of operations:'LOP_INSERT_ROWS','LOP_DELETE_ROWS','LOP_MODIFY_ROW'
具体解析代码如下:
--解析日志
create function dbo.f_splitBinary(@s varbinary(8000))
returns @t table(id int identity(1,1),Value binary(1))
as
begin
declare @i int,@im int
select @i=1,@im=datalength(@s)
while @i<=@im
begin
insert into @t select substring(@s,@i,1)
set @i=@i+1
end
return
end
GO
create function dbo.f_reverseBinary(@s varbinary(128))
returns varbinary(128)
as
begin
declare @r varbinary(128)
set @r=0x
select @r=@r+Value from dbo.f_splitBinary(@s) a order by id desc
return @r
end
GO
create proc [dbo].[p_getLog](@TableName sysname,@c int=100)
/*
解析日志
sample: p_getLog 'tablename';
*/
as
set nocount on
declare @s varbinary(8000),@s1 varbinary(8000),@str varchar(8000),@str1 varchar(8000),@lb int,@le int,@operation varchar(128)
declare @i int,@lib int,@lie int,@ib int,@ie int,@lenVar int,@columnname sysname,@length int,@columntype varchar(32),@prec int,@scale int
declare @TUVLength int,@vc int,@tc int,@bitAdd int,@bitCount int,@count int
select b.name,b.length,c.name typename,b.colid,b.xprec,b.xscale,
case when c.name not like '%var%' and c.name not in ('xml','text','image') then 1 else 2 end p,row_number() over(partition by
case when c.name not like '%var%' and c.name not in ('xml','text','image') then 1 else 2 end order by colid) pid
into #t
from sysobjects a inner join syscolumns b on a.id=b.id inner join systypes c on b.xtype=c.xusertype
where a.name=@TableName order by b.colid
SELECT top(@c) Operation,[RowLog Contents 0],[RowLog Contents 1],[RowLog Contents 2],[RowLog Contents 3],[Log Record],id=identity(int,1,1) into #t1
from::fn_dblog (null, null)
where AllocUnitName like 'dbo.'+@TableName+'%'and
Operation in('LOP_INSERT_ROWS','LOP_DELETE_ROWS','LOP_MODIFY_ROW' )
AND Context not in ('LCX_IAM','LCX_PFS')
order by [Current LSN] desc
select @tc=count(*) from #t
select @lb=min(id),@le=max(id) from #t1
while @lb<=@le
begin
select @operation=Operation,@s=[RowLog Contents 0],@s1=[RowLog Contents 1] from #t1 where id=@lb AND [RowLog Contents 1] IS NOT NULL
set @TUVLength=convert(int,dbo.f_reverseBinary(substring(@s,3,2)))+3
select @i=5,@str='',@vc=0,@bitCount=0
select @lib=min(pid),@lie=max(pid) from #t where p=1
while @lib<=@lie
begin
select @columnname=name,@length=length,@columntype=typename,@prec=xprec,@scale=xscale,@vc=colid-1 from #t where p=1 and pid=@lib
-- if @columntype<>'bit'
-- print rtrim(@i)+'->'+rtrim(@length)
if dbo.f_reverseBinary(substring(@s,@TUVLength,1+((@tc-1)/8))) & power(2,@vc) <> 0
begin
if @columntype<>'bit'
select @str=@str+@columnname+'=NULL,',@i=@i+@length
else
begin
select @str=@str+@columnname+'=NULL,'
set @bitAdd = case when @bitCount=0 then @i else @bitAdd end
set @bitCount = (@bitCount + 1)%8
set @i=@i+case @bitCount when 1 then 1 else 0 end
-- print rtrim(@bitAdd)+'->'+rtrim(@length)
end
end
else if @columntype='char'
select @str=@str+@columnname+'='+convert(varchar(256),substring(@s,@i,@length))+',',@i=@i+@length
else if @columntype='nchar'
select @str=@str+@columnname+'='+convert(nvarchar(256),substring(@s,@i,@length))+',',@i=@i+@length
else if @columntype='datetime'
select @str=@str+@columnname+'='+convert(varchar,dateadd(second,convert(int,dbo.f_reverseBinary(substring(@s,@i,4)))/300
,dateadd(day,convert(int,dbo.f_reverseBinary(substring(@s,@i+4,4))),'1900-01-01')),120)+',',@i=@i+8
else if @columntype='smalldatetime'
select @str=@str+@columnname+'='+convert(varchar,dateadd(minute,convert(int,dbo.f_reverseBinary(substring(@s,@i,2)))
,dateadd(day,convert(int,dbo.f_reverseBinary(substring(@s,@i+2,2))),'1900-01-01')),120)+',',@i=@i+4
else if @columntype='int'
select @str=@str+@columnname+'='+rtrim(convert(int,dbo.f_reverseBinary(substring(@s,@i,4))))+',',@i=@i+4
else if @columntype='decimal'
select @str=@str+@columnname+'=DECIMAL,',@i=@i+@length
else if @columntype='bit'
begin
set @bitAdd = case when @bitCount=0 then @i else @bitAdd end
set @bitCount = (@bitCount + 1)%8
select @str=@str+@columnname+'='+rtrim(convert(bit,substring(@s,@bitAdd,1)&power(2,case @bitCount when 0 then 8 else @bitCount end-1)))+','
,@i=@i+case @bitCount when 1 then 1 else 0 end
-- print rtrim(@bitAdd)+'->'+rtrim(@length)
end
set @lib=@lib+1
end
set @i=convert(int,dbo.f_reverseBinary(substring(@s,3,2)))+4+((@tc-1)/8)
set @lenVar=convert(int,dbo.f_reverseBinary(substring(@s,@i,2)))
set @i=@i+2
set @ib=@i + @lenVar*2
set @ie=convert(int,dbo.f_reverseBinary(substring(@s,@i,2)))
set @count=0
select @lib=min(pid),@lie=max(pid) from #t where p=2
while @lib<=@lie
begin
-- print rtrim(@ib)+'->'+rtrim(@ie)
select @columnname=name,@length=length,@columntype=typename,@vc=colid-1 from #t where p=2 and pid=@lib
if dbo.f_reverseBinary(substring(@s,@TUVLength,1+((@tc-1)/8))) & power(2,@vc) <> 0
begin
select @str=@str+@columnname+'=NULL,'
select @ib=@ie+1,@i=@i+2
if @count<@lenVar
set @ie=convert(int,dbo.f_reverseBinary(substring(@s,@i,2)))
end
else if @columntype='varchar'
begin
select @str=@str+@columnname+'='+convert(varchar(256),substring(@s,@ib,@ie-@ib+1))+','
select @ib=@ie+1,@i=@i+2
set @ie=convert(int,dbo.f_reverseBinary(substring(@s,@i,2)))
end
else if @columntype='nvarchar'
begin
select @str=@str+@columnname+'='+convert(nvarchar(256),substring(@s,@ib,@ie-@ib+1))+','
select @ib=@ie+1,@i=@i+2
set @ie=convert(int,dbo.f_reverseBinary(substring(@s,@i,2)))
end
set @count=@count+1
set @lib=@lib+1
end
set @str=left(@str,len(@str)-1)
IF @operation ='LOP_MODIFY_ROW'
BEGIN
set @TUVLength=convert(int,dbo.f_reverseBinary(substring(@s1,3,2)))+3
select @i=5,@str1='',@vc=0,@bitCount=0
select @lib=min(pid),@lie=max(pid) from #t where p=1
while @lib<=@lie
begin
select @columnname=name,@length=length,@columntype=typename,@prec=xprec,@scale=xscale,@vc=colid-1 from #t where p=1 and pid=@lib
-- if @columntype<>'bit'
-- print rtrim(@i)+'->'+rtrim(@length)
if dbo.f_reverseBinary(substring(@s1,@TUVLength,1+((@tc-1)/8))) & power(2,@vc) <> 0
begin
if @columntype<>'bit'
select @str1=@str1+@columnname+'=NULL,',@i=@i+@length
else
begin
select @str1=@str1+@columnname+'=NULL,'
set @bitAdd = case when @bitCount=0 then @i else @bitAdd end
set @bitCount = (@bitCount + 1)%8
set @i=@i+case @bitCount when 1 then 1 else 0 end
-- print rtrim(@bitAdd)+'->'+rtrim(@length)
end
end
else if @columntype='char'
select @str1=@str1+@columnname+'='+convert(varchar(256),substring(@s1,@i,@length))+',',@i=@i+@length
else if @columntype='nchar'
select @str1=@str1+@columnname+'='+convert(nvarchar(256),substring(@s1,@i,@length))+',',@i=@i+@length
else if @columntype='datetime'
select @str1=@str1+@columnname+'='+convert(varchar,dateadd(second,convert(int,dbo.f_reverseBinary(substring(@s1,@i,4)))/300
,dateadd(day,convert(int,dbo.f_reverseBinary(substring(@s1,@i+4,4))),'1900-01-01')),120)+',',@i=@i+8
else if @columntype='smalldatetime'
select @str1=@str1+@columnname+'='+convert(varchar,dateadd(minute,convert(int,dbo.f_reverseBinary(substring(@s1,@i,2)))
,dateadd(day,convert(int,dbo.f_reverseBinary(substring(@s1,@i+2,2))),'1900-01-01')),120)+',',@i=@i+4
else if @columntype='int'
select @str1=@str1+@columnname+'='+rtrim(convert(int,dbo.f_reverseBinary(substring(@s1,@i,4))))+',',@i=@i+4
else if @columntype='decimal'
select @str1=@str1+@columnname+'=DECIMAL,',@i=@i+@length
else if @columntype='bit'
begin
set @bitAdd = case when @bitCount=0 then @i else @bitAdd end
set @bitCount = (@bitCount + 1)%8
select @str1=@str1+@columnname+'='+rtrim(convert(bit,substring(@s1,@bitAdd,1)&power(2,case @bitCount when 0 then 8 else @bitCount end-1)))+','
,@i=@i+case @bitCount when 1 then 1 else 0 end
-- print rtrim(@bitAdd)+'->'+rtrim(@length)
end
set @lib=@lib+1
end
set @i=convert(int,dbo.f_reverseBinary(substring(@s1,3,2)))+4+((@tc-1)/8)
set @lenVar=convert(int,dbo.f_reverseBinary(substring(@s1,@i,2)))
set @i=@i+2
set @ib=@i + @lenVar*2
set @ie=convert(int,dbo.f_reverseBinary(substring(@s1,@i,2)))
set @count=0
select @lib=min(pid),@lie=max(pid) from #t where p=2
while @lib<=@lie
begin
-- print rtrim(@ib)+'->'+rtrim(@ie)
select @columnname=name,@length=length,@columntype=typename,@vc=colid-1 from #t where p=2 and pid=@lib
if dbo.f_reverseBinary(substring(@s1,@TUVLength,1+((@tc-1)/8))) & power(2,@vc) <> 0
begin
select @str1=@str1+@columnname+'=NULL,'
select @ib=@ie+1,@i=@i+2
if @count<@lenVar
set @ie=convert(int,dbo.f_reverseBinary(substring(@s1,@i,2)))
end
else if @columntype='varchar'
begin
select @str1=@str1+@columnname+'='+convert(varchar(256),substring(@s1,@ib,@ie-@ib+1))+','
select @ib=@ie+1,@i=@i+2
set @ie=convert(int,dbo.f_reverseBinary(substring(@s1,@i,2)))
end
else if @columntype='nvarchar'
begin
select @str1=@str1+@columnname+'='+convert(nvarchar(256),substring(@s1,@ib,@ie-@ib+1))+','
select @ib=@ie+1,@i=@i+2
set @ie=convert(int,dbo.f_reverseBinary(substring(@s1,@i,2)))
end
set @count=@count+1
set @lib=@lib+1
end
set @str1=left(@str1,len(@str1)-1)
END
IF @operation ='LOP_MODIFY_ROW'
BEGIN
print @operation+'修改前值: '+@str
print @operation+'修改后值:'+@str1
END
ELSE
BEGIN
print @operation+':'+@str
END
set @lb=@lb+1
END
drop table #t,#t1
GO