好一段时间没写博客了,这次我们来一起谈谈SQL文件执行器的功能实现,在ERP软件升级时往往在客户端程序更新的同时也要对数据库进行升级,ERP程序开发人员会对数据库升级的执行代码在开发的过程中以SQL文件的形式记录下来或者保存到特定格式的文件中供软件升级时使用,有些ERP软件会附带开发数据库升级工具来方便实施人员执行软件升级操作或者ERP软件内置数据库升级功能,不管使用什么样的方式能达到软件升级的目的就是好方法,这次我们就来剥离这部分的功能来实现一个SQL文件执行器,不特定于SQL文件,只要文件里面包含有SQL语法,而且能正常得到执行,在本执行器中就能正确的执行它(本次我们主要争对SQL文件,其他格式文件只要把相关控制去掉就好了,只是得不到很好的控制比较乱,或者你有更好的方法)。介绍性引入就到此为止了,接下来我们开始进入主题,研究一下SQL执行器的原理及设计思路吧。

一、SQL文件执行器原理分析

在分析原理之前,我们先来规范一下SQL文档的书写,我们在一个SQL语句结束的时候换行来个GO关键字再换行继续书写下一个SQL语句,遇到“USE [数据库名] ”语句时希望能把此语句单独放一行。做到以上书写规范我们开始原理分析:

1.上述SQL规范居然都做到了但是SQL文件里面的内容还不是我想要的,我想重新规范一下我的SQL文档,我想理想化它,所以我需要对SQL文件进行重新洗牌,我想我应该一行一行的阅读它,并把它提取出来去掉前后的空格,我应该重组改文件的内容并且在读取每一行的同时在其末尾写入一个换行回车符,而且我还想统计出每一个SQL文件中的SQL语句的个数(循环时会用到),我用GO关键字来标记了SQL语句的个数,在阅读到一行去掉首尾空格后只剩下不区分大小写的GO时我的统计会+1(统计从1开始),哦,这还永远不够,我还没考虑到USE关键字的处理,通常会选择一个数据库来执行相应的SQL文件,USE关键字在切换着数据库,我需要在检测到USE语句时切换数据库为USE后面跟着的数据库来执行后面的代码,所以我要把USE语句单独剥离开来以待做特殊的处理,往往我们写SQL文件的时候在USE语句的前后都不带GO关键字的,我需要给它加上。有一个需要注意的地方:USE [数据库名]  后面直接跟着SQL语句的(没有回车换行),这种写法在语法上是完全正确的,但是看上去就不是很美观了,这种方式我这边就不做处理了,请遵守上述规范吧,再处理下去程序性能就严重下降啦,本程序在数据库切换时是从USE 之后的字符开始到回车换行符结束来取数据库名的,这种写法会引发SQL异常。我需要构建一个这样的方法。

2.在第一点里面我们对SQL文件进行了格式化,现在开始我希望以GO关键字作为分割点,把SQL文件里面的SQL语法进行分段,我希望一段段的得到执行并返回执行结果,此时我需要一个循环来遍历文件中的SQL语句并执行它。本工具名字叫做SQL文件执行器很显然是争对批量SQL文件的处理的,所以一个循环是永远不够的,我还得在外面再套一个循环来遍历所有的SQL文件,对每个SQL文件进行分析并遍历其中的SQL语句执行它,这样就达到我们的目的了。貌似还有一个问题未处理,比如在执行到一半的时候我不执行了需要强制停止他这如何是好呢,强制关闭程序很显然是不可取的很容易引发未知的数据库异常或者造成数据丢失这些状况都是我们不想看到的,那么有什么好办法呢?此时我启用了臭名远扬的goto语句来从深层嵌套循环中跳出循环,我让他在用户发出停止指令后在执行完当前的SQL语法段后跳出循环,从而停止接下来的SQL语法的执行,这样子保障了SQL数据的安全,在SQL文件执行的期间,程序是不允许关闭的,除非向程序发出停止指令,并成功停止的时候,才允许程序关闭。

很简单的一个程序,我就大致的做以上两点的原理分析吧,接下来我上传下我的程序界面设计图吧,大家参考下:

接下来我贴上来一些主要源代码供参考,代码可能看上去有些难懂,表述性不是很好,期待大家来改进它:

这是对SQL文件中的语法进行重新洗牌的方法

        /// <summary>
/// 读取文件内容(SQL关键字特殊处理主要针对GO关键字)
/// </summary>
/// <param name="path">文件路径</param>
/// <param name="keywords">关键字</param>
/// <param name="str">输出字符串</param>
/// <param name="i">keywords的个数</param>
public void FileReader(string path,string keywords,out int i,out string str)
{
bool useplusgo = false;//use后面是否跟着go
bool goplususe = false;//use前面是否存在go
StreamReader sr = new StreamReader(path, Encoding.GetEncoding("GB2312"));
//str = sr.ReadToEnd();
string s = null;
string temp = null;
int x = 0;
while ((temp = sr.ReadLine()) != null)
{
if (temp.Trim().ToUpper() == keywords.ToUpper())
{
useplusgo = false;//use语句后面跟着go则关闭use判断
goplususe = true;//use前面存在go
x++;
s += "\r\n" + keywords + "\r\n \r\n";
}
else if (temp.Trim().Length >= 4 && temp.Trim().ToUpper().Substring(0, 4).Trim() == "USE")
{
temp = temp.Trim().Replace("[", "").Replace("]", "");
//如果use前面不存在go则加上
if (!goplususe && s != "\r\n" && s != null)
{
x++;
s += "\r\n" + keywords + "\r\n\r\n" + temp + "\r\n";
}
else
{
s += temp + "\r\n";
}
useplusgo = true;
}
else
{
goplususe = false;//go后面不跟use关闭判断
//如果use后面不跟go则加上go
if (useplusgo)
{
x++;
s += "\r\n" + keywords + "\r\n" + temp + "\r\n";
useplusgo = false;//关闭use判断
}
else
{
s += temp + "\r\n";
}
}
}
i = x;
str = s;
sr.Close();//关闭当前打开的文件
}

这个是处理USE语句的方法

        /// <summary>
/// 获取use后面的数据库名称
/// </summary>
/// <param name="str">use字符串行</param>
/// <returns></returns>
public string UseStatementProcessing(string str)
{
string[] strSplit = Regex.Split(str, "\r\n", RegexOptions.IgnoreCase);
int x = strSplit.Length;
string s = "";
if (/*strSplit[0].ToUpper().IndexOf("USE ", 0) >= 0*/Regex.IsMatch(strSplit[0].ToUpper(), "USE ", RegexOptions.IgnoreCase))
{
s = strSplit[0].Substring(4, strSplit[0].Length - 4).Trim();
}
else if (/*strSplit[1].ToUpper().IndexOf("USE ", 0) >= 0*/Regex.IsMatch(strSplit[1].ToUpper(), "USE ", RegexOptions.IgnoreCase))
{
s = strSplit[1].Substring(4, strSplit[1].Length - 4).Trim();
}
else
{
s = "";
}
return s;
}

这次话题就到此为止吧,这程序比较简单,大家可以写写玩,当作练练手也不错,主要在于文件的操作和字符串的处理。

C# SQL文件执行器的功能实现的更多相关文章

  1. SQL脚本文件执行器

    处于项目需求,需要能够批量执行SQL脚本文件,需要由前台页面操作触发执行. 查找相关资料,发现 Ant 提供了 SQLExec 组件可以支持SQL文件的执行,测试效果不错. 以下是对 SQLExec ...

  2. mysql 导入导出.sql文件

    备份数据库(包含全部表和全部存储过程):C:\Documents and Settings\Administrator>mysqldump -h localhost -u root -p -R ...

  3. 批量执行SQL文件

    原文:批量执行SQL文件 摘要:很多时候我们在做系统升级时需要将大量的.sql文件挨个执行,十分不方便.而且考虑到执行顺序和客服的操作方便性,能不能找到一种简单的方法来批量执行这些sql文件呢? 主要 ...

  4. SQL点滴7—使用SQL Server的attach功能出现错误及解决方法

    原文:SQL点滴7-使用SQL Server的attach功能出现错误及解决方法 今天用SQL Server 2008的attach功能附加一个数据库,出了点问题,提示的错误是: Unable to ...

  5. java 网络编程之TCP通信和简单的文件上传功能

    */ .hljs { display: block; overflow-x: auto; padding: 0.5em; color: #333; background: #f8f8f8; } .hl ...

  6. navicat导入.sql文件

    用Navicat for Mysql导入.sql文件   虽然这算不上什么难事,但是对于新手来说(比如说我),Navicat for MySQL里的导出连接.运行SQL文件.导入向导.还原备份.这些功 ...

  7. oracle导入大sql文件

    最近遇到一个需要导入大SQL文件的问题,最先直接用SQL developer 导入大SQL文件,结果报IO Exception,只好采用sqlplus 导入,操作过程如下: sqlplus 用户名/密 ...

  8. [.NET开发] C#实现的SQL备份与还原功能示例

    本文实例讲述了C#实现的SQL备份与还原功能.分享给大家供大家参考,具体如下: //记得加 folderBrowserDialog1 openFileDialog1 控件 using System.D ...

  9. PHP导出sql文件

    发现自己之前写的php导出sql数据为Excel文件在导出一些数据的时候出现了精度的问题,比如导出身份证号的时候会把后面变成0000.暂时先把这个问题留下,有空去看看到底是什么问题. 写了一个导出sq ...

随机推荐

  1. 论山寨手机与Android联姻 【5】MTK颠覆手机产业链

    MTK一站式解决方案(Turn-Key)模式出现以前,手机设计开发流程大约可以分成以下6步. 第1步,Design House从芯片厂商那里拿到参考设计. 芯片厂商根据自己的市场部门对手机市场的预测, ...

  2. 一步一步学习SignalR进行实时通信_5_Hub

    原文:一步一步学习SignalR进行实时通信_5_Hub 一步一步学习SignalR进行实时通信\_5_Hub SignalR 一步一步学习SignalR进行实时通信_5_Hub 前言 Hub命名规则 ...

  3. Java_SSH项目主要步骤记录

    建立Spring-Struts-Hibernate的步骤整理 1. 建立web project 2. 建立hernate, action, service包 3. 右击项目,add myeclipse ...

  4. GDKOI 2016

    GDKOI 2016 day 1 第一题 魔卡少女 题目描述:维护一个序列,能进行以下两个操作:1.将某一个位置的值改变.2.求区间的连续子串的异或值的和. solution 因为序列的数的值都小于\ ...

  5. ckeditor编辑器在java项目中配置

    一.基本使用: 1.所需文件架包 A. Ckeditor基本文件包,比如:ckeditor_3.6.2.zip 下载地址:http://ckeditor.com/download 2.配置使用 A.将 ...

  6. 剖析magento中关于Email模板的设置

    public function send()    {        $emailTemplate = Mage::getModel('core/email_template');        // ...

  7. C# 中奇妙的函数–7. String Split 和 Join

    很多时候处理字符串数据,比如从文件中读取或者存入 - 我们可能需要加入分隔符(如CSV文件中的逗号),或使用一个分隔符来合并字符串序列. 很多人都知道使用split()的方法,但使用与其对应的Join ...

  8. 使用Vitamio打造自己的Android万能播放器(5)——在线播放(播放优酷视频)

    前言 为了保证每周一篇的进度,又由于Vitamio新版本没有发布, 决定推迟本地播放的一些功能(截图.视频时间.尺寸等),跳过直接写在线播放部分的章节.从Vitamio的介绍可以看得出,其支持http ...

  9. JavaScript 工作必知(九)function 说起 闭包问题

    大纲 Function Caller 返回函数调用者 Callee 调用自身 作用域 闭包 function 函数格式 function getPrototyNames(o,/*optional*/ ...

  10. iOS 打开系统设置

    NSURL*url=[NSURL URLWithString:@"prefs:root=WIFI"]; [[UIApplication sharedApplication] ope ...