前言
数据导入作为系统常用的功能,几乎所有的系统都应该支持,主要用于系统初期,大批量初始化数据,或者需要输入的数据行比较多的情况(导入在建项目的交付清单,通常成百上千项)。
数据导入的一般过程是,用户按照模板格式Excel文件,然后程序读取这个文件,根据列进行对应,逐行将数据写入到系统中。
一,先讲讲模板:
一般使用Excel模板,所谓模板就是约定Excel的每一列对应系统的哪一列的值,对应方式有两种,一是根据顺序匹配,二是根据列名匹配。所谓根据顺序匹配,是约定第一列(A列)对应系统数据库的那一列,B列对应哪一列。这种模板可以没有标题行,也可以有多行标题。所谓列名匹配,是指定列的名字进行匹配,这种情况需要有标题行。匹配时要指明是否忽略掉标题中的空格,*号,因为有些模板中会使用*来表示该列是必输列,也有可能有空格。
还有,可能还需要指定标题或者数据的起始行数,因为有时候数据并不一定从第2行开始,有可能模板数据有个大大的标题,比如“xx单位培训上报名单”,其后才是标题行和数据行。
除了excel形式的模板,还有.csv格式,所谓“CSV”,是Comma Separated Value(逗号分隔值)的英文缩写,通常都是纯文本文件。就是列与列之间用逗号分隔,现在不是很常用了。其实.csv文件也可以用Excel打开的。
系统模板下载。用户要使用模板导入数据,首先先要获得模板,系统可以提供一个模板下载的功能,可以在数据导入界面或者一个统一的下载界面,提供模板下载功能,这样,用户就不用到处找模板在哪里,打电话给你要模板了。推荐在数据导入界面直接下载模板。并且,系统应该根据导入方案自动生成模板,而不是人为加工再上传到服务器。这样就极大避免了模板过期,与方案不匹配的问题。
二,导入
导入方式有很多种,下面逐个讨论之。
1,编写通用的服务端导入程序
对一些简单的没有检查逻辑的数据,可以写通用的导入程序,这样效率还高。比如我们的条码系统就写了一个通用的excel导入。效率还是很高的。通用导入程序,定义模板文件与数据库表之间的对应关系,还有其他的一些逻辑,比如根据编号查找名称,带出其他列等等。也可以定义数据检查逻辑。系统读入excel文件,将数据传送到服务端,服务端要用数据导入程序,逐行读数数据,根据定义的检查逻辑检查数据是否合法,并将数据写入到指定的表。这种导入程序,有很多中复杂的情况需要处理:
(1)自动编码。
一般我们录入订单或者入库单的时候,单号可以自动形成,根据一定的形成规则,这种规则一般是通过后台程序来实现的,导入程序要提供这方面的支持,比如,定义某个编码列,以及引用的编码规则,指导导入程序调用适当的编号形成程序形成代码。
(2)根据A列带出B列的内容。
比如,根据输入的员工号,带出员工所属的部门代码。这种业务场景,还需要定义列写入的顺序。先代出,然后可以覆盖(带出的为默认值)。
(3)根据名称找编码。
比如导入员工表,员工所属部门,数据库中保存的是编号,但是模板中可能是填写的部门名称。就需要根据名称找到编码,写到数据库中。导入程序就要定义查找关系。
(4)导入范围。
上面的根据名称找编码的逻辑看上去很简单,但是实际应用中还有个更复杂的情况,设想再一个集团企业中,不同的分支机构中的部门可能是相同的(人力资源部都叫人力资源部),在不同的机构中导入,就应该仅仅查找该机构下的编码。这就涉及到数据导入范围的概念。要抽象出数据导入范围这个概念,而且导入范围的限定还不一定来自一个条件,可能是多个条件。导入程序定义界面需要指定导入范围由什么来确定,比如用组织机构;定义好导入范围后,比如按照组织机构来限定范围。导入时要提供导入范围选择界面,并把导入范围选择的值应用到导入逻辑中(比如在根据部门名称查找部门编码时,加上组织机构的限制)。
(5)定义”无值“的表示方式。
考虑下手工录入的场景,某一列的内容我完全可以不录入,导入时,如何区分”不录入“这种场景呢,可以定义一个特殊的值,比如”<n/a>“表示该单元格不导入。因为在某些场景下(比如更新模式),是需要区分”不修改“还是”修改为空“这两种情况的。
(6)数据的合法性检查逻辑。
导入时,要先检查数据是否合法,这就需要定义一种检查逻辑或者说是语法。比如编码存在性检查,数据格式检查(数字列输入了非数字值),数据宽度,数据范围(只能输入0/1/2/3),甚至有的列的检查依赖于其他列的值,比如证件号码的合法性,依赖于证件类型:身份证,港澳通行证,士兵证等等。有些有效性可以通过定义正则表达式来检查,有些就比较复杂了。希望大家学习下正则表达式的知识。
2,界面模拟录入的导入方式
第一种方式,大家可以发现,需要定义大量的规则,而这些规则,在界面录入编码过程中都已经实现了,如果能充分利用已经实现的这些规则,将会避免大量的重复性工作,并且有些逻辑可能通过第一种方式实现不了。
这种情况一般是采用模拟键盘输入的方式,从模板中读取数据,填到界面的特定录入框,然后界面自然会调用已经实现的逻辑,复用了程序代码。
这两种方式各有优缺点。第一种方式优点是效率很高,适合大批量的数据导入。以前做过一个项目,需要的导入大量的数据,由于时间比较紧急,刚开始采用第二种方式,每秒才能导入不到1条数据。1000条数据就要差不多20分钟,用户不干了,因为要导入的数据不止1000行。不得已才采用了第1种方式。但第一种方式定义起来麻烦。第二种方式的优缺点与第一种正好相反。而且对界面编程要求比较高 。在做输入界面的时候,就要提前考虑好这种导入,给这种模拟输入的导入提供方便。
无论哪种方式,都要提供模拟导入的功能,以便检查数据错误。
第一种方式,通过数据检查逻辑,对数据进行检查,并提供详细的检查报告。并且导入结束后,能将导入失败的原因写入到源文件的对应行,这样方便用户修正。
第二种方式,可以提供模拟导入的功能,吧错误检查出来,同样,也能将检查出来的错误,写入到源文件中。
至于选择那种方式,可以根据具体的业务场景进行抉择。数据校验少,不复杂的,对效率要求高的,选择第一种方式,否则选择第二种方式。
导入时,还可以支持过滤条件,比如只导入数据源中符合条件的某些数据。
其他
应该支持同时导入多个模板文件。导入时,同时选择多个excel文件,逐个排队导入。
三,导入的其他复杂情况
1,导入父子表
一般的业务凭证,都是父子表结构,比如财务凭证,有凭证头,多条分录。这就是典型的父子表。更复杂的还有多个、多级父子表。
可以将这种多级关系抽闲成数据行的级次情况,,或通过列缩进的方式来体现父子关系。
其实,你看如果按照上面的格式定义,导入程序是能工作的。
但是!这样会将模板搞的忒复杂了,易用性不好。其实excel导入最适合的就是简单的二维表,没有这么复杂的结构。否则就适得其反了,光是输入这么复杂的表,就够麻烦的了,何不直接在系统中录入呢。
一般这种父子表的导入可以采用多次导入的方式来实现,先导入表头,再导入明细。表头和明细使用不同的模板。
2、导入多个表
有时候在系统设计的时候,业务上的一个数据可能会保存到多个物理表中。那么在导入的时候,就需要支持这种场景,你不能让用户导入多次。在定义导入的时候,指定多个目标表,然后在定义导入目标列时,可以从多个表中选择。多个表通过数据的KEY关联。如果KEY是自动形成的,那么只在导入第一个表时自动生成,导入后边的表时,直接拿过来用就好了。
3、导入组的概念
上面导入父子表的例子,最后给出的方案是表头和明细定义不同模板,分别导入的方案。这种方案是可行的,但是用户实际用起来稍有点麻烦,需要做两次导入操作。其实除了导入这种父子表的例子,还有一种场景,抽象讲,我导入了A表的内容,同时希望在B表中也插入部分数据;另外的场景就是上面说的,导入多个表。
以上场景,我们可以定义多个导入方案,然后把多个导入方案组合在一起,组成一个“导入组”,自动按顺序执行导入组中的每个方案。满足以上的场景。方案组中的每个方案的数据可以来自不同的数据文件,也可以来自同一个文件。
4,导入文件附件
确实有这种需求,比如导入商品主数据,系统可能需要给每个商品配个照片,这个照片存放在本地文件系统,用户希望在导入模板中输入图片的地址。
导入图片就不像模拟键盘输入这么简单了,需要特殊的导入程序。这就需要专门根据这种特殊的需求来发专门的程序了,由导入程序来调用,也有可能是录入界面来调用(如果是在单据录入界面导入的话)。可以开发一种支持附件导入的程序,通过约定附件的制定格式(比如在模板中制定附件的位置)
5,将导入结果回写到excel模板中
将导入结果反写到excel中,便于用户查看,方便用户做后续额的处理,比如显示某条数据是否导入成功,如果没有导入成功,显示失败的原因,便于用户根据错误原因,调整数据。数据模板中可以提供两个固定的列(导入状态,状态说明),前者存放是否导入成功,成功,显示“成功”,失败显示失败,并在导入文本中显示具体的失败原因,可能是数据部符合要求,也可能是系统出错了等等。并提供一个导入进度指示,比如显示一个列表,列出所有需要导入的数据行,根据导入的进度,滚动这个列表,并在列表中实时显示导入结果,成功,失败,失败原因等等。