中曾提到要编写可阅读的代码,VBA学习笔记02 (
分类:巴黎人-数据库

这个警告在常规场景中没什么影响,但如果是用excel跑SQL,它会因为该警告阻止你的后续操作~事实上excel执行sql限制多多,需要更多的奇技淫巧,之前我就写过一篇。言归正传,要解决这个警告,一种当然是在语句中用到聚合函数的地方统统加上isnull,但如果语句很长,地方很多就蛋疼了,于是我推荐另一个更优雅的做法:

怎样往mysql中导数据执行效率高

VBA学习笔记

Ruby操作excel文件首先需要在脚本里包含以下语句

require 'win32ole'

我在 关于极简编程的思考 中曾提到要编写可阅读的代码。因为代码是编写一次,阅读多次。 阅读者包括代码编写者,以及后来的维护人员。能让阅读代码更轻松,有利于增强项目或者产品的可维护性。

只需语句顶部加一句:

 

笔记摘抄自EXCEL精英培训-蓝色幻想

打开excel文件,对其中的sheet进行访问:

excel = WIN32OLE::new('excel.Application')
workbook = excel.Workbooks.Open('c:examplesspreadsheet.xls')
worksheet = workbook.Worksheets(1) #定位到第一个sheet
worksheet.Select

本博客分为上下俩部分,第一部分讲解在代码层次 编写可阅读的代码,参考地址是https://my.oschina.net/xiandafu/blog/1509679

SET ANSI_WARNINGS OFF;

问题,给你一个Excel数据文件,需要往mysql的数据库中导入数据。

VBA学习笔记01(链接)
VBA学习笔记02 (链接)

读取数据:

worksheet.Range('a12')['Value']  #读取a12中的数据
data = worksheet.Range('a1:c12')['Value'] #将数据读入到一个二维表

这一部分讲解方法,类,以及一些设计上的考虑,这些考虑并不是来自于某些设计原则或者是设计模式,而是基于对象的职责,将在下面会讲述

搞掂。

首先,你得按照对应表字段对excel数据文件进行构造,然后转化为insert的sql语句,然后往数据库中插入。

目录

找到第一处a列的值为空值

line = 1
while worksheet.Range("a#{line}")['Value']
   line=line+1
end #line的值为第一处空白行的行数

发现对象

在上半部分,我们讲到一个解析excel的例子,在我实际项目里,曾经是这个样子

public void parse(Sheet sheet,StringBuilder error){

User user = readUserInfo(sheet,error);
List<Order> orders = readUserOrderInfo(sheet,error);
UserCredit credit = readUserCreditInfo(sheet,error);

}

之所以提供一个StringBuilder 参数,是因为需求是如果解析出错,需要显示出错的的位置,项目开发人员因此将错误信息拼接成字符串,最后返回给前端。

如果审查其实现,你会发现该解析方法到处都是类似如下代码

error.append("在"+line+"和”+col+“列错":+"messsage).append("n");

这两段代码的阅读者困惑之处就是error定义不能说明如何处理解析错误,阅读者不得不看清楚具体实现才恍然大悟--原来我的前任用StringBuilder是想这么干。另外一个困惑之处就是在解析excel的时候,就已经写死了错误输出的样子,如果想更改,就需要改每一处地方
,我们知道业务的excel解析,几百行代码算是少的了。要阅读者几百行代码重构对后来者并非易事。

有什么模式或者设计原则能解决这个吗?

我想说的是,并没有模式和设计原则能解决,开发者缺少的仅仅是发现和归纳对象的能力(设计模式是锦上添花),对于excel解析的错误信息,实际上就应该定义一个”错误信息“这样的对象。比如

public class ExcelParseError{
    public void addError(ParseErrorInfo info ){}
    public void addSimpleError(String line,String col,String message ){}
    public List<ParseErrorInfo> getMessages(){}
    public String toString(){
        .....
    }
}

因此,excel解析最后是这个样子

public void parse(Sheet sheet,ExcelParseError error){
User user = readUserInfo(sheet,error);
List<Order> orders = readUserOrderInfo(sheet,error);
UserCredit credit = readUserCreditInfo(sheet,error);

}

处理解析错误的代码则变成如下

error.addSimpleError(line,col,message);

- EOF -

最开始没考虑执行效率,我转化为sql语句后,用navicat作为数据库查看的界面,然后新建查询,将构造好的sql语句粘到里面,执行,然后sql语句开始疯跑,3万多条记录,执行了八百多秒,十四分钟啊,太慢了,当时没注意。后来,发现导入的数据有些地方因为excel格式而产生问题,于是又重新构造,再往数据库中导。又是漫长的等待。。。

CH1 VBA基础知识

将第一列的值读入到一个数组中

line = '1'
data = []
while worksheet.Range("a#{line}")['Value']
   data << worksheet.Range("a#{line}:d#{line}")['Value']
   line.succ!
end

再次发现对象

发现对象是让杂乱代码变得有序的最重要方式,看如下例子:

public Long  startWorkflow(String user,long orgId,long taskType,long workflowType,Map<String,String> taskParas){
    .....
}

这是一个工作流引擎启动流程的API,共有5个参数。这是我曾经项目的最早定义的API,后来实际上又扩展了好几个参数,比如工作流支持版本后,又需要增加一个参数是int workflowVersion。

这6个参数实际上代表了启动工作流需要的三类参数,"工作流参与人的描述","工作流本身的描述",还有"工作流启动的输入参数",因此,这个API最终定义成

public Long  startWorkflow(Participant p,WorkflowDef workflow,Variable vars){
    .....
}

Participant对应了工作流参与人描述
WorkflowDef 对应了工作流定义
Variable 则对应了工作流参数

这些对象增强了API的可扩展性,更为重要的是,他的代码更加容易阅读,无论是调用者,还是api本身的实现,"新发现的对象"让杂乱无章的变量变得有序起来.

对象是在我们编程生活中真实存在的,如果能感知到对象存在,则编程会美好很多,同样,阅读和维护代码也会更加方便。在没有感知对象的情况下妄谈设计模式和和设计原则,就是无源之水。

下一个例子是我的BeetlSQL的例子,有一个SQLLoader类用来加载sql语句,其中有一个片段是
从markdown 文件加载sql语句。最初代码如下(警告,代码有毒,不要阅读,直接跳过)

bf = new BufferedReader(new InputStreamReader(ins));
String temp = null;
StringBuffer sql = null;
String key = null;
while ((temp = bf.readLine()) != null) {
    if (temp.startsWith("===")) {// 读取到===号,说明上一行是key,下面是SQL语句
        if (!list.isEmpty() && list.size() > 1) {// 如果链表里面有多个,说明是上一句的sql+下一句的key
            String tempKey = list.pollLast();// 取出下一句sql的key先存着
            sql = new StringBuffer();
            key = list.pollFirst();
            while (!list.isEmpty()) {// 拼装成一句sql
                sql.append(list.pollFirst() + lineSeparator);
            }
            this.sqlSourceMap.put(modelName + key, new SQLSource(
                    sql.toString()));// 放入map
            list.addLast(tempKey);// 把下一句的key又放进来
        }
    } else {
        list.addLast(temp);
    }
}
// 最后一句sql
sql = new StringBuffer();
key = list.pollFirst();
while (!list.isEmpty()) {
    sql.append(list.pollFirst());
}
this.sqlSourceMap.put(modelName + key,
        new SQLSource(sql.toString()));

这段代码解析markdown文件,读取以===分割的的sql片段,并放到sqlSourceMap里。大概格式如下

    disableUser
    ===
    * 这是一个更新用户信息的SQL语句
    update user set status = 1 where id = #id#

尽管解析代码不算长,且有很多注释,但每次在这里增加一点扩展都极其困难。比如Markdown 支持 ”*“ 符号作为注释语句,那对"*"代码解析放在个哪个地方?

后来我对这段代码进行重构了,实际上,我是发现我需要一个MDParser类来负责这事情 :专门解析md文件,MDParser定义如下(可以阅读了)

public class MDParser {
    public MDParser(String modelName,BufferedReader br) throws IOException{
        this.modelName =  modelName;
        this.br = br;
        skipHeader();
    }
    public void skipHeader() throws IOException{
    ....
    }

    public SQLSource next() throws IOException{
        String sqlId = readSqlId();
        if(status==END){
            return null;
        }
        //去掉可能的尾部空格
        sqlId = sqlId.trim();
        skipComment();
        if(status==END){
            return null;
        }
        int sqlLine = this.linNumber;
        String sql = readSql();

        SQLSource source = new SQLSource(modelName + sqlId,sql);
        source.setLine(sqlLine);
        return source;
    }
}

从这个类可以看到,当读入一个markdown文件的时候,首选调用skipHeader,去掉md文件开头无关的文档整体说明

next方法用来获取每一个sql片段说明,先调用 readSqlId获取sql的标示符号,然后 skipComment方法用来忽略sql注释,最后 readSql用来读取sql语句内容。

MDParser 使得SQLLoader更加精简和容易阅读,也使得关于Markkdown 解析更加容易维护。

于是开始思考:将一张表导出为sql语句再执行、将整个数据库导出再执行好像并没有这么慢啊!

CH2 VBA函数与公式

将数据写入到excel表格中

worksheet.Range('e2')['Value'] = Time.now.strftime '%d/%m/%Y' #单个值
worksheet.Range('a5:c5')['Value'] = ['Test', '25', 'result']  #将一个数组写入

警惕String,数组,和 Map

当程序中出现String 参数,数组参数,以及Map的时候,已经在提醒我们是遗漏了系统的对象。
这三个类型参数当然非常灵活,能容纳下任何数据结构,但有可能遗漏了系统隐含的对象。尤其是数组和Map。我在上一章提到过的例子

Object[] rets = call();
boolean  success = (Boolean)rets[0];
String msg = (String)rets[1];

就没有下面的定义好

CallResult rets = call();
boolean  success = rets.isSuccess();
String msg =  rets.getMessage();

如果CallResult包含了某个返回值,那么,将CallResult定义成泛型就更加容易阅读,比如返回CallResult

public CallResult  getUser(){

}

这肯定没有如下代码更容易阅读,让后来者放心去使用

public CallResult<User>  getUser(){

}

这一篇我提到的每一个好的例子都相对于差的的例子,都会多写数行代码,甚至还得写一个类
,但毫无疑问,阅读更加容易,维护更加方便了。

我将sql语句制作成一个sql文件,以文件的方式执行,果然,十几秒钟就执行完毕。

CH3 VBE编辑器

调用宏定义

excel.Run('SortByNumber')

总结 如果只能用一个设计模式

我做过大量业务系统,电信的也好,金融也好,互联网项目,还是创业项目,也写过不少工具,能公开的比如有Beetl,BeetlSQL,XLSUnit。这么多工程项目,如果让我说最重要的设计技巧是什么,或者只能用一个设计技巧,我会毫不犹豫的说,是”职责模式“

职责模式 描述了如何发现和划分对象职责,就好比一个班,应该有班长,各科学习委员,小组长.
再比如,新闻里经常出现某某重大事故,就会成立了某某专项委员会。在比如,为了保证项目质量,我们有测试组,为了监控项目,我们有PMO。我们周围生活,一直都按照人尽其职,职责划分这个原则来运作。 如果划分错了,非常影响我们的生活,比如让我去监控项目进度:(。

职责模式,可以搜索 GRASP

这是一个很少被人提起的模式,我个人推荐去学习体会。

卢正雨在《绝世高手》里,从屌丝最后变成了食神,如果你看了这个电影,就知道,他成为食神是因为对食物的细腻感知。我想在《自下向上的编写容易阅读的代码方法》这一部分的总结是
”感知对象的存在“,你也能写出容易阅读的代码,甚至成为高手。

结论:以文件形式执行sql语句比新建查询语句执行sql语句效率高得多。

CH4 分支与END语句

设置背景色

worksheet.Range('a3:f5').Interior['ColorIndex'] = 36 #pale yellow

问题,给你一个Excel数据文件,需要往mysql的数据库中导入数据。 首先,你得按照对应表字段对excel数据文件...

CH5 文件操作

<br />


<br />

将背景色恢复成无色

worksheet.Range('a3:f5').Interior['ColorIndex'] = -4142 # XlColorIndexNone constant

本文由巴黎人手机版发布于巴黎人-数据库,转载请注明出处:中曾提到要编写可阅读的代码,VBA学习笔记02 (

上一篇:没有了 下一篇:没有了
猜你喜欢
热门排行
精彩图文