前几天为工作中的一个实际问题编写了一个小工具,但应该说“麻雀虽小,五脏俱全”,同样涉及到了设计、编码、测试几个过程,并且其中涉及到一些有趣的知识,拿出来和大家分享一下。
首先来说明一下想要解决的问题:
当前每个月公司中的某个部门都会在特定的一天(一般是月初)下载大量的数据,这些数据是从数据库中取得的,生成文本格式的文件,然后用来做后续的处理,类似于EDI的程序。但是,后续处理的程序需要所有的记录都是经过排序的,而现在的下载数据的程序是这样做的,直接写带有Order by的SQL语句,然后直接生成数据。但是这样的话,程序运行的时间非常长,因为在服务器中的内存中进行排序,会耗费大量的资源。这还不是主要问题,主要的问题在于下载数据的时候会对其他部门进行系统操作造成极大的影响,甚至于会造成系统的停顿和没有响应。
因此我考虑在下载数据的时候不对数据进行排序,而是把排序工作拿出来,用一个专门的小程序来实现,这样就可以即使会造成性能下降也只局限在运行小工具程序的那台机器,而不会对公司其他用户造成影响。(后来发现对本机的性能也没有太大的影响)。
遵循这个思路,我所需要做的就是开发一个专门用来对这些数据排序的小工具。为了提高工作的效率,我是用VS 2005来开发,尽管公司中大多数项目使用的都是Java,呵呵。
首先是如何对文本文件中的数据进行排序,其实很简单,我只是将这个文本文件作为数据源,配置在ODBC中了。
然后呢,在VS 2005的服务器管理器中就可以配置对这个数据源的访问。
但是,此时会有两个问题:
一是对身份证号码这样的字段,系统会默认为是浮点数,这样查询出来看到的记过是科学计数法的形式,而实际上我想要的是文本的类型。
这个问题可以使用配置数据源的Schema来解决,
里面可以将每个字段都指定为文本类型,这样就方便导出了。
但是随之而来的就会有第二个问题,那就是这个文件必须指定文件名,而每次需要排序的文件名都是不一样的。
对这个问题,我是这样解决的,先将需要排序的文件copy到这个临时目录中,并指定固定的文件名,然后在这个目录中针对固定的文件名进行字段类型的配置就可以了。针对这个文件排序之后,生成一个排序好的文本文件,然后再把这个文件复制到指定的目标文件中。
这样基本上技术问题就都解决掉了。最后是对界面的一个设计,先看一下界面:
这里我设置了一个小技巧,当用户选择了源文件之后,我会自动根据这个名字,在后面加上_Sorted,作为输出的文件名,这样就方便用户了。
最后,下面是以下关键的代码:
// 将选择的文件名称显示在源文件的输入框中
OpenFileDialog openSourceFile = new OpenFileDialog();
initializeFileOpenDialog(openSourceFile);
if (openSourceFile.ShowDialog() == DialogResult.OK)
{
String fileName = openSourceFile.FileName;
txtSourceFile.Text = fileName;
// 如果文件名不为空,则自动生成目标文件的名称
if ( ! fileName.Trim().Equals(String.Empty))
{
int extendNamePosition = fileName.IndexOf( " . " );
String targetFileName = fileName.Substring( 0 , extendNamePosition) + " _Sorted " + " . " + fileName.Substring(extendNamePosition + 1 );
txtTargetFile.Text = targetFileName;
}
}
private void btnSort_Click( object sender, EventArgs e)
{
if (txtSourceFile.Text.Trim().Equals(String.Empty))
{
MessageBox.Show( " 请先选择源文件。 " );
return ;
}
if (txtTargetFile.Text.Trim().Equals(String.Empty))
{
MessageBox.Show( " 请先指定目标文件名。 " );
return ;
}
// 先将源数据复制到指定的文件夹中
// 然后以该文件夹中的数据作为数据源进行操作
CurrentJobInfo.Text = " 正在将文件复制到临时文件夹中…… " ;
CopyFileToTempPath();
// 对临时文件夹中的数据进行排序
CurrentJobInfo.Text = " 正在排序…… " ;
Database db = DatabaseFactory.CreateDatabase( " DataStore " );
String strSql = CreateSql();
DbCommand dbCommand = db.GetSqlStringCommand(strSql);
// 准备输出的文件
String sortedFileName = getSortedFileName();
FileInfo sortedFile = new FileInfo(TEMP_DATA_PATH + sortedFileName);
StreamWriter writer = sortedFile.CreateText();
// 将数据写入到文件中
using (IDataReader dr = db.ExecuteReader(dbCommand))
{
while (dr.Read())
{
StringBuilder sbRecord = new StringBuilder();
for ( int i = 0 ; i < dr.FieldCount - 1 ; i ++ )
{
sbRecord.Append(dr[i].ToString() + " , " );
}
sbRecord.Append(dr[dr.FieldCount - 1 ].ToString());
writer.WriteLine(sbRecord.ToString());
}
}
writer.Close();
// 将排序好的文件输出到目标文件中
File.Delete(txtTargetFile.Text);
File.Copy(TEMP_DATA_PATH + sortedFileName, txtTargetFile.Text);
CurrentJobInfo.Text = " 完成! " ;
}
希望对这个问题的解决思路能够对大家解决类似的问题有些帮助,也希望大家一起来讨论,:)