c#读取并分析sql Server2005数据库日志
用过logExplorer的朋友都会被他强悍的功能吸引,我写过一篇详细的操作文档可以参考
http://blog.csdn.net/jinjazz/archive/2008/05/19/2459692.aspx
我们可以自己用开发工具来实现sql日志的读取,这个应用还是很酷的,具体思路
1、首先要了解一个没有公开的系统函数::fn_dblog,他可以读取sql日志,并返回二进制的行数据
2、然后要了解sql的二进制数据是如何存储的,这个可以参考我的blog文章
http://blog.csdn.net/jinjazz/archive/2008/08/07/2783872.aspx
3、用自己擅长的开发工具来分析数据,得到我们需要的信息
我用c#写了一个测试样例,分析了int,char,datetime和varchar的日志情况而且没有考虑null和空字符串的保存,希望感兴趣的朋友能和我一起交流打造属于自己的日志分析工具
详细的试验步骤以及代码如下:
1、首先建立sqlserver的测试环境,我用的sql2005,这个过程不能保证在之前的版本中运行
以下sql语句会建立一个dbLogTest数据库,并建立一张log_test表,然后插入3条数据之后把表清空
- use master
- go
- create database dbLogTest
- go
- use dbLogTest
- go
- create table log_test(id int ,code char(10),name varchar(20),date datetime,memo varchar(100))
- insert into log_test select 100, 'id001','jinjazz',getdate(),'剪刀'
- insert into log_test select 65549,'id002','游客',getdate()-1,'这家伙很懒,没有设置昵称'
- insert into log_test select -999,'id003','这家伙来自火星',getdate()-1000,'a'
- delete from log_test
- --use master
- --go
- --drop database dbLogTest
2、我们最终的目的是要找到被我们删掉的数据
3、分析日志的c#代码:我已经尽量详细的写了注释
- using System;
- using System.Collections.Generic;
- using System.Text;
- namespace ConsoleApplication21
- {
- class Program
- {
- /// <summary>
- /// 分析sql2005日志,找回被delete的数据,引用请保留以下信息
- /// 作者:jinjazz (csdn的剪刀)
- /// 作者blog:http://blog.csdn.net/jinjazz
- /// </summary>
- /// <param name="args"></param>
- static void Main(string[] args)
- {
- using (System.Data.SqlClient.SqlConnection conn = new System.Data.SqlClient.SqlConnection())
- {
- conn.ConnectionString = "server=localhost;uid=sa;pwd=sqlgis;database=dbLogTest";
- conn.Open();
- using (System.Data.SqlClient.SqlCommand command = conn.CreateCommand())
- {
- //察看dbo.log_test对象的sql日志
- command.CommandText = @"SELECT allocunitname,operation,[RowLog Contents 0] as r0,[RowLog Contents 1]as r1
- from::fn_dblog (null, null)
- where allocunitname like 'dbo.log_test%'and
- operation in('LOP_INSERT_ROWS','LOP_DELETE_ROWS')";
- System.Data.SqlClient.SqlDataReader reader = command.ExecuteReader();
- //根据表字段的顺序建立字段数组
- Datacolumn[] columns = new Datacolumn[]
- {
- new Datacolumn("id", System.Data.SqlDbType.Int),
- new Datacolumn("code", System.Data.SqlDbType.Char,10),
- new Datacolumn("name", System.Data.SqlDbType.VarChar),
- new Datacolumn("date", System.Data.SqlDbType.DateTime),
- new Datacolumn("memo", System.Data.SqlDbType.VarChar)
- };
- //循环读取日志
- while (reader.Read())
- {
- byte[] data = (byte[])reader["r0"];
- try
- {
- //把二进制数据结构转换为明文
- TranslateData(data, columns);
- Console.WriteLine("数据对象{1}的{0}操作:", reader["operation"], reader["allocunitname"]);
- foreach (Datacolumn c in columns)
- {
- Console.WriteLine("{0} = {1}", c.Name, c.Value);
- }
- Console.WriteLine();
- }
- catch
- {
- //to-do...
- }
- }
- reader.Close();
- }
- conn.Close();
- }
- Console.WriteLine("************************日志分析完成");
- Console.ReadLine();
- }
- //自定义的column结构
- public class Datacolumn
- {
- public string Name;
- public System.Data.SqlDbType DataType;
- public short Length = -1;
- public object Value = null;
- public Datacolumn(string name, System.Data.SqlDbType type)
- {
- Name = name;
- DataType = type;
- }
- public Datacolumn(string name,System.Data.SqlDbType type,short length)
- {
- Name = name;
- DataType = type;
- Length = length;
- }
- }
- /// <summary>
- /// sql二进制结构翻译,这个比较关键,测试环境为sql2005,其他版本没有测过。
- /// </summary>
- /// <param name="data"></param>
- /// <param name="columns"></param>
- static void TranslateData(byte[] data, Datacolumn[] columns)
- {
- //我只根据示例写了Char,DateTime,Int三种定长度字段和varchar一种不定长字段,其余的有兴趣可以自己补充
- //这里没有暂时没有考虑Null和空字符串两种情况,以后会补充。
- //引用请保留以下信息:
- //作者:jinjazz
- //sql的数据行二进制结构参考我的blog
- //http://blog.csdn.net/jinjazz/archive/2008/08/07/2783872.aspx
- //行数据从第5个字节开始
- short index = 4;
- //先取定长字段
- foreach (Datacolumn c in columns)
- {
- switch (c.DataType)
- {
- case System.Data.SqlDbType.Char:
- //读取定长字符串,需要根据表结构指定长度
- c.Value = System.Text.Encoding.Default.GetString(data,index,c.Length);
- index += c.Length;
- break;
- case System.Data.SqlDbType.DateTime:
- //读取datetime字段,sql为8字节保存
- System.DateTime date = new DateTime(1900, 1, 1);
- //前四位1/300秒保存
- int second = BitConverter.ToInt32(data, index);
- date = date.AddSeconds(second/300);
- index += 4;
- //后四位1900-1-1的天数
- int days = BitConverter.ToInt32(data, index);
- date=date.AddDays(days);
- index += 4;
- c.Value = date;
- break;
- case System.Data.SqlDbType.Int:
- //读取int字段,为4个字节保存
- c.Value = BitConverter.ToInt32(data, index);
- index += 4;
- break;
- default:
- //忽略不定长字段和其他不支持以及不愿意考虑的字段
- break;
- }
- }
- //跳过三个字节
- index += 3;
- //取变长字段的数量,保存两个字节
- short varColumnCount = BitConverter.ToInt16(data, index);
- index += 2;
- //接下来,每两个字节保存一个变长字段的结束位置,
- //所以第一个变长字段的开始位置可以算出来
- short startIndex =(short)( index + varColumnCount * 2);
- //第一个变长字段的结束位置也可以算出来
- short endIndex = BitConverter.ToInt16(data, index);
- //循环变长字段列表读取数据
- foreach (Datacolumn c in columns)
- {
- switch (c.DataType)
- {
- case System.Data.SqlDbType.VarChar:
- //根据开始和结束位置,可以算出来每个变长字段的值
- c.Value =System.Text.Encoding.Default.GetString(data, startIndex, endIndex - startIndex);
- //下一个变长字段的开始位置
- startIndex = endIndex;
- //获取下一个变长字段的结束位置
- index += 2;
- endIndex = BitConverter.ToInt16(data, index);
- break;
- default:
- //忽略定长字段和其他不支持以及不愿意考虑的字段
- break;
- }
- }
- //获取完毕
- }
- }
- }
4、更改你的sql连接字符串后运行以上代码,会看到如下输出信息:
- 数据对象dbo.log_test的LOP_INSERT_ROWS操作:
- id = 100
- code = id001
- name = jinjazz
- date = 2008-8-7 18:14:03
- memo = 剪刀
- 数据对象dbo.log_test的LOP_INSERT_ROWS操作:
- id = 65549
- code = id002
- name = 游客
- date = 2008-8-6 18:14:03
- memo = 这家伙很懒,没有设置昵称
- 数据对象dbo.log_test的LOP_INSERT_ROWS操作:
- id = -999
- code = id003
- name = 这家伙来自火星
- date = 2005-11-11 18:14:03
- memo = a
- 数据对象dbo.log_test的LOP_DELETE_ROWS操作:
- id = 100
- code = id001
- name = jinjazz
- date = 2008-8-7 18:14:03
- memo = 剪刀
- 数据对象dbo.log_test的LOP_DELETE_ROWS操作:
- id = 65549
- code = id002
- name = 游客
- date = 2008-8-6 18:14:03
- memo = 这家伙很懒,没有设置昵称
- 数据对象dbo.log_test的LOP_DELETE_ROWS操作:
- id = -999
- code = id003
- name = 这家伙来自火星
- date = 2005-11-11 18:14:03
- memo = a
- ************************日志分析完成
c#读取并分析sql Server2005数据库日志的更多相关文章
- 收缩sql server数据库日志
项目中,可能数据库(sql server数据库)日志太多,占了很多磁盘空间,可以通过收缩数据库日志,减少日志文件大小. 下面以Northwind数据库为例: 1.把数据库的恢复模式设置为“简单模式”: ...
- SQL 收缩数据库日志的几种办法 (2005与2008 略有区别)
在SQL Server 2000/2005中可以快速压缩日志log文件,通过SQL, 方法一: ---DBTEST 为数据库名,顺序最好别乱.注意:要先截断再清空,最后收缩! backup log D ...
- MS SQL SERVER 数据库日志压缩方法与代码
MS SQL性能是很不错的,但是数据库用了一段时间之后,数据库却变得很大,实际的数据量不大.一般都是数据库日志引起的!数据库日志的增长可以达到好几百M. DUMP TRANSACTION [数据库名] ...
- 查询SQL SERVER数据库日志工具
在SQL SERVER中查看操作日志,一直是一个比较麻烦的事情,因为微软并没有提供直接的系统工具可以查看日志内容,虽然可以通过非正式的隐藏接口dbcc log 获取日志的非解析编码但是要还原是个非常麻 ...
- 解决SQL server2005数据库死锁的经验心得
前段时间提到的"sql server 2005 死锁解决探索",死锁严重,平均每天会发生一次死锁,在解决和处理SQL server2005死锁中查了很多资料和想了很多办法,后来我们 ...
- SQL Server 数据库开启日志CDC记录,导致SQL Server 数据库日志异常增大
这几天单位的SQL Server业务数据生产库出现数据库日志增长迅速,导致最终数据无法写入数据库,业务系统提示"数据库事务日志已满",经过多方咨询和请教,终于将日志异常的数据库处理 ...
- 纯C++ 连接SQL Server2005 数据库读写操作的小例子
一个测试c++链接 sql server 数据库的例子// 数据库说明// 数据库用户为 sa , 密码为 空// 数据库为 MyDB// 表为 UserInfo// 表字段为 Name . Pass ...
- sql server2016 数据库日志 清空语句
/*1.查询数据库日志文件名称*/--TC_MES_DEV为数据库名--这里的 数据库日志名,可以用以下注释的语句进行查询(_log那个)USE [TC_MES_DEV]GOSELECT file_i ...
- SQL SERVER2008 数据库日志文件的收缩方法
最近公司的数据库随着业务量的增多,日志文件巨大(超过300G),造成磁盘空间不够用,进而后来的访问数据库请求无法访问. 网上类似的方法也很多,但不可行,如下是我实践过,可行的,将日志文件收缩至任意指定 ...
随机推荐
- JS表单提交的几种方式
第一种方式 : 表单提交,在 form 标签中增加 onsubmit 事件来判断表单是否提交成功 <script type="text/javascript"> fun ...
- 虚拟机中ubuntu-16.04 Linux系统下配置mysql数据库,并在windows下使用navicat远程连接
Linux系统下mysql数据库安装配置步骤: 1.在服务器上安装mysql:sudo apt-get install mysql-server sudo apt-get install mysql- ...
- ubuntu环境下lnmp环境搭建(1)之Mysql
1. vm下安装Ubuntu 1)下载镜像ubuntu-15.04-desktop-amd64.iso http://yunpan.cn/cF5dwV6zw33ef 访问密码 ecba(个人分享在36 ...
- 在jsp页面的js中使用Cookie的原理介绍以及相应方法的代码
1. 设置cookie 1.1 每个cookie都是一个名/值对,可以把下面这样一个字符串赋值给document.cookie: document.cookie="user_Id=828&q ...
- BZOJ-4915-简单的数字题
Description 对任意的四个不同的正整数组成的集合A={a_1,a_2,a_3,a_4 },记S_A=a_1+a_2+a_3+a_4,设n_A是满足a_i+a_j (1 ≤i<j≤4)| ...
- NOIP初赛 之 逻辑运算
NOIP初赛 之 逻辑运算 逻辑运算先掌握各种运算,注意运算符的级别比较,做题是要细心.在NOIP中一般一题,分值为1.5分. 概念介绍: 非:not ¬ 与:and ∧ 或:o ...
- C++的socket编程学习
前言 不得不承认作为一个前端开发,仍有一个后台开发的梦.从socket通信开始学习,在工作之余补充学习点相关知识,记录下学习的过程. 服务端 服务器代码如下,在设置listen之后,通过accept获 ...
- cocos2dx - tmx地图分层移动处理
接上一节内容:cocos2dx - 节点管理 瓦片地图(Tiled Map) 在cocos2dx文档中有简单的介绍及使用.详情可以看:http://www.cocos2d-x.org/docs/man ...
- win10 uwp 打包第三方字体到应用
有时候我们会把一些特殊字体打包到软件,因为如果找不到我们的字体会变为默认,现在很多字体图标我们用得好,有时候我们的应用会用很漂亮的字体,需要我们自己打包,因为用户一般是没有字体. UWP使用第三方字体 ...
- (七)php运算符
一:算数运算符 +(加).-(减).*(成)./(除) %(取模,求余的意思) <?php $a=7/3; echo $a; //2.3333333333333.因为float类型的最大精度为1 ...