模拟测试数据的生成方法

28次阅读

共计 10486 个字符,预计需要花费 27 分钟才能阅读完成。

【摘要】许多程序员都头疼测试数据的模拟,一个是要逼真,另一个需要数据量,不逼真往往导致一些 bug 测不出来,数据量不够则无法发现性能问题,这篇文章给出了很好的解决办法。1)、应用系统或软件产品一般都需要进行不同阶段的验证工作,包括原型功能论证、功能测试、性能测试等,这些测试、论证场景都可能涉及到测试数据的准备。2)、根据用户的业务需求、数据预置约束条件、数据间层级关联等条件,生成对应的模拟验证数据。一般来说,按照用户要求模拟数据应该做到:数据量可控、充分的随机性、保持数据间特定的逻辑关联关系(包括直接关联或隐含关联)。3)、本文我们将介绍一个方便、灵活的模拟数据生成工具——集算器。去乾学院看个究竟吧!模拟测试数据的生成方法
模拟测试数据的生成方法
应用系统或软件产品一般都需要进行不同阶段的验证工作,包括原型功能论证、功能测试、性能测试等,这些测试、论证场景都可能涉及到测试数据的准备。测试数据有时可以直接复用历史数据,但很多情况下,基于历史数据建立的测试数据可能会出现内容缺失不全、数据量级不够、数据涉密不能导出、数据已加密无法参与计算等情况,这时就需要根据用户的业务需求、数据预置约束条件、数据间层级关联等条件,生成对应的模拟验证数据。一般来说,按照用户要求模拟数据应该做到:数据量可控、充分的随机性、保持数据间特定的逻辑关联关系(包括直接关联或隐含关联)。
单数据表是最常见的数据模拟情况,这种表由主键和普通字段组成,也可以由无主键的普通字段组成。在单数据表的基础上,可以进一步进行多数据表的关联模拟,关联关系可以归类分为:外键关联、同维关联、主子关联。对于关联数据生成的原则具体可参见《怎样生成有关联的测试数据》一文。对于已经生成的数据,则可以参考《优化 Join 运算的系列方法》对模拟生成的数据进行更进一步地深入查询和分析处理。
一般来说,单数据表模拟数据的难点是对数据表字段内容的灵活生成。而多数据表模拟则是在此基础上考虑关联关系(一层或多层)的生成,尤其是需要保证多数据表在进行关联过滤之后,还能够有充足、有效的结果数据,从而满足数据的关联运算或是其它展现需求。实际情况中,通过编程生成这些模拟验证数据的难点通常是数据间复杂的逻辑关联关系。
本文中,我们将介绍一个方便、灵活的模拟数据生成工具——集算器。集算器是一款面向应用程序员和数据分析员,专注于结构化数据分析与处理的快速开发工具,是一套基于 Java 解释执行的动态语言,采用了先进的计算模型和设计思想,让开发更易于实现、性能更好。同时,集算器还具备完备的类库和轻量级架构,足以让数据模拟生成更加灵活和高效。
一、引言
集算器具有跨平台、无框架、易部署的特点,仅需要安装有 JAVA 虚拟机的操作系统即可,特别是对于数据模拟生成的过程,可以随时分步调试查看中间过程数据。
集算器使用 SPL 程序语言。SPL(Structured Process Language)是一种面向(半)结构化数据计算的程序设计语言,能够满足复杂处理和过程计算的数据处理需求。
同时,集算器还可以直接将模拟生成的数据落地在本地磁盘的二进制“集文件”(集算器自定义的一种文件格式)中,免去繁琐的安装数据库的操作,这也符合模拟测试相对临时性的工作特点。集文件本身具有使用简单、可追加、支持大数据、可分段并行等特点,而且很容易转成其它格式,如数据库、文本等。当然,集算器自身也支持直接或通过集文件将模拟数据导入到任意目标数据库中,所需的具体外部库的使用可以参见《外部库》函数参考章节。
为了方便叙述和验证,本文默认模拟生成的数据均落地在本地集文件中。
1、为什么使用集文件?
在用集算器生成模拟数据时,常用两种文件格式:文本文件和集文件。
文本是各种数据平台 / 数据库都支持的文件格式,具有良好的通用性。但文本文件的查询性能较差,占用磁盘空间也较大,而且缺少字段的数据类型定义,有时可能会出现“类型歧义”的错误。
针对这些问题,集算器设计了一种二进制格式文件,称为集文件(文件后缀 btx)。集文件中使用了低 CPU 消耗的压缩编码,数据存储时较文本文件占用磁盘空间更小,具有较高的查询性能,并且字段的数据类型也被存储,避免出现类型歧义。同时,集文件继承了文本文件支持大数据量、可追加和易于分段并行的特点。因此,在需要使用数据文件时,集文件是更好的选择。
2、如何使用集文件?
利用集文件存储模拟生成的数据,因为其具有与其它数据格式广泛的互通性,后续就可以灵活进行与目标数据源的双向转化,包括 Oracle、DB2、MS SQL、MySQL、PG 等关系型数据库和 TXT/CSV、JSON/XML、EXCEL 等文件类型。
下面首先针对常用的文本文件、MySQL 进行双向互转的说明。
1)  集文件与文本文件互转
    集文件可以与文本文件进行互相转换,相应的 SPL 实现脚本示例如下:

A1:导入文本文件数据。使用 cursor@t() 游标方式读入“文本.txt”文件数据,其中 @t 指定将第一行记录作为字段名, 如果不使用 @t 选项就会以_1,_2,…作为字段名。
A2:使用循环函数,将文本数据循环追加到集文件中。使用 export@ab() 将文本数据导出到集文件中,其中,@a 表示追加写数据到集文件, 如果不用 @a 就表示重建集文件,@b 表示导出为集文件。
在集算器中可以直接查看生成的“文本转集文件.btx”文件数据结果,如下图所示:

A3:导入集文件数据。使用 cursor@b() 游标方式读入“集文件.btx”,其中 @b 表示将导入的是集文件。
A4:使用循环函数,将集文件数据循环追加到文本文件中。export@t() 将指定单元格的数据导出到文本文件中,其中,@a 表示追加写数据到文本文件中, 不用 @a 则表示重建文本文件,@t 表示将集文件的字段名作为第一条记录写入文本文件。
查看生成的“集文件转文本数据.txt”文本文件数据结果,如下图所示:

2)  集文件与 MySQL 互转
集文件也可以与 MySQL 之间进行数据互转,相应的 SPL 脚本也很简单。下例将 MySQL 数据库 employeeinfo 表中的数据导出到集文件中:

A1:连接 MySQL 数据源,使用 connect() 进行 MySQL 数据库的连接。如果用鼠标点击 A1 单元格,可以直接查看 MySQL 数据库的连接信息。具体查看数据库配置教程相关章节文档的配置说明。
A2:游标读取 MySQL 中 employeeinfo 表数据。
A3:使用循环函数,将 A2 读出的数据循环追加到集文件中。使用 export@az() 将文本数据导出到集文件中,其中,@a 表示追加写数据到集文件中, 如果不用 @a 则表示重建集文件,@z 是将数据强制导出集文件,注意,这里用了 @z 而不是前面的 @b,事实上 @z 会强制导出集文件(和 @b 效果一样),同时还可以通过表达式 export@z(A2;s) 增加 s 选项作为分组表达式,s 对于文本文件为自选分隔符, 缺省默认分隔符是 tab,有 s 参数时认为 A2 文本数据对 s 有序,仅在 s 变化时才分段,这种设置分段的集文件用于并行数据量大时的分段导出, 导出时同一段的记录不会被拆开。缺省情况下不分段导出“文本转集文件.btx”集文件。
同样,在集算器中可以直接查看生成的“MySQL 集文件.btx”文件数据结果,如下图所示:

A4:使用 close() 函数关闭 A1 建立起的 MySQL 数据源连接。
下例则是将集文件中的数据插入到 MySQL 数据库 employeeinfo 表中:

A1:连接 MySQL 数据源。使用 connect() 进行 MySQL 数据库的连接。如果用鼠标点击 A1 单元格,可以直接查看 MySQL 数据库的连接信息。具体查看数据库配置教程相关章节文档的配置说明。
A2:游标方式导入集文件的数据。集文件数据使用 cursor@b() 将以游标方式读取,其中包含数据表字段:empid 和 other。
A3:更新 MySQL 数据库“employeeinfo”库表中的数据。使用 update() 将单元格 A2 通过游标读取的集文件数据更新到 MySQL 数据库“employeeinfo”库表中。
A4:使用 close() 函数关闭 A1 建立起的 MySQL 数据源连接。
使用第三方工具查看 MySQL 数据库的“employeeinfo”库表,插入的数据结果如下截图所示:

二、模拟生成字段数据
在开始单数据表模拟之前,我们先了解一下通过 SPL 脚本处理主键字段和普通字段的方法。
1、主键字段数据的模拟生成
主键字段最典型的要求就是模拟生成的数值不能重复,另外可能会要求一定的顺序、范围、格式要求。
1)  数值自增
对于主键字段,常见的要求就是字段值唯一、不重复,且自增:

A1:新建导出的集文件。
A2:定义模拟生成的数据量。
A3:批量模拟生成数据。其中 empid 是模拟数据表的主键数值自增字段。使用“#”序号值直接设置主键 empid 的自增数值,使用 rand() 函数生成 0-100000 内的随机数模拟 other 字段。
A4:将单元格 A3 生成的数据导出到 A1 的新建集文件中。使用 export@b() 将数据追加写入到 btx 集文件数据中,其中,@b 表示导出集文件格式。
点击 A1 单元格生成的“数据自增表.btx”集文件,查看文件数据结果如下:

2)  在规定数据范围内取值且不重复
主键字段也可以是某个规定范围内不重复的数值,比如从 1~10000 中批量取 1000 个不重复的数值作为主键字段,SPL 脚本如下:

A1:模拟数据范围 n
A2:模拟需要获取的数据数量 m。
A3:数据范围 n 不大时获取 m 个不重复数值的方法。如果 n 不大,那么对于包含 n 个成员的集合排序很快,这时可以先用 to() 函数生成一个从 1 到 n 的序列,然后使用 sort() 函数基于 rand() 函数生成的随机数直接将这个序列的成员顺序随机打乱,然后再通过 to() 获取 m 个不重复数值。
查看单元格 A3 的结果,如下图所示:

A4:如果数据范围 n 很大,那么对个成员的集合排序就会比较慢,因此就不能使用 A3 的方法了。我们的做法是在数据范围 n 内随机获取 m*1.1 个数值,然后使用 id() 函数将这 m*1.1 个随机获取的数据去重。由于随机取数可能会有重复,如果只取 m 个,去重后就会少于 m 个,所以这里定义了一个系数 1.1,适当多取几个,确保在去重后,剩余数量多于 m 个。然后在 B4 中通过 to() 获取其中的 m 个。
查看单元格 A4 的结果,如下图所示,一共取得了 1044 个不重复的数(有 56 个重复被去重去掉了):

再查看单元格 B4 的结果,如下图所示,正好是 1000 个:

3)  字符串主键
主键字段还有一种情况是由“特定编号 + 序号 / 随机数”(日期的情况暂不在此讨论)组成,也就是常见的编码形式,相应的 SPL 模拟脚本如下:

A1:新建导出的集文件。其中将使用 empid 作为模拟数据表的字符串主键字段。
A2:定义模拟生成的数据量。
A3:批量模拟生成数据。使用字符串拼接出主键字段 empid。empid 由固定字符串“RAQ”标识、三位数序号,以及 10 位随机数拼接而成。其中,三位数序号字符串使用 string() 对获取的序号进行格式化处理,用“0”补足三位;用函数 concat() 将序列 10.(rand(10)) 中的 10 个数字成员用无分隔符连接,拼接成 10 位随机数字组成的字符串。另外,依旧使用 rand() 函数生成 0-100000 内的随机数填入 other 字段。
A4:将生成数据导出为集文件格式,与上例相同。
点击 A1 单元格生成的“字符串编码表.btx”集文件查看文件数据结果,如下图所示:

特别地,红框中标示的就是生成的三位序号字符串。
4)  大数据量的流式追加生成
上面的主键生成都不涉及复杂的主键字段生成,而且当小数据量时基本只要一行循环函数代码,全内存执行就可以了。但是当要求生成的数据量巨大,例如千万、亿级以上,或是生成的数据在内存中放不下的时候,我们就需要考虑分次生成追加记录了。
这时候就需要用 for 循环进行显式控制,分批生成数据。每次流式写入多行,而不是一行一行写。下面我们看一下使用 SPL 编写脚本如何实现大数量的生成与处理:

A1:新建导出的集文件。其中将用 empid 作为模拟数据表的字符串主键字段,用 other 作为普通字段。
A2:定义生模拟生成的数据量和每次分批生成的数据。
A3:循环生成数据,计算出分批生成数据的循环次数并进行循环。
B4:按照单元格 A3 设定的循环分批生成具体数据,其中使用“(A3-1)*C2+~”生成主键序号。
B5-B6:将大据量分批流式写入集文件格式,并清空分批的临时数据。使用 export@ab()将生成的数据流式追加写入到集文件,@a 表示追加数据,@b 表示导出集文件格式。最后,在单元格 B6 中使用 null 清空单元格 B4 中的数据。
例子中单元格 A3-B6 四行代码,实现了大数据的分批流式追加写入,能够有效防止内存溢出。
查看生成的 1 千万数据量的 ” 大数据量的流式追加生成.btx” 集文件的数据内容,如下图所示:

2、普通字段数据的模拟生成
1)  常规日期
SPL 对日期时间的处理提供了丰富的函数支持,详见日期时间函数参考,下面是两个例子。

A1:获取当前年份的第一天日期。使用 now() 函数获取当前系统时间,使用 year 获取年份,使用 date(year,month,day) 函数获取年份的第 1 天日期。
A2:随机生成指定年份的日期。使用 elapse() 函数计算相差某个时间的新日期数据,即与新年第一天相差 n 天的日期。其中,days@y() 是获得指定日期所在年的天数。
点击 A2 单元格,可以看到某次运行的数据结果是:
2)  规定数据范围可重复的值
模拟的字段在规定数据范围内随机取值,由于是非主键字段,值可以重复,如下例所示:

A1:随机生成一个 10 位数字的字符串。查看某次运行结果为:
A2:在字符 0-9 之间随机生成 10 位数字的字符串。这里使用 rands() 函数取得一个随机字符串。从结果看 A2 与 A1 的结果都是生成 10 位显示数字的字符串,可以根据实际需求对生成的结果值进行数据类型转换处理,如:字符串转数值等。
A3~A6:在行政区划中随机取值,其中:
A3:导入本地包含“行政区划代码”数据的文本文件。
查看该表数据可以看到包含 id 编号和区域名称。

A4:使用 len() 函数获取行政区划代码数据表长度,然后在此范围内随机获取一个序号。
A5:根据 A4 的序号返回 A3 的“行政区划代码”数据表的指定位置的 id 数据。
A6:获取指定位置数据的 id 字段值。

3)  字段拆分与组合
某些字段的构成需要遵循一定的编码规则,例如身份证号码,我们通过公民身份证号码可以很直接的获取一个人所在的地区、出生日期、性别,并计算出年龄。这里就以此为例对字段的拆分和组合做详细的说明,其它类似的情况可以以此为参考。:
公民身份证号码是特征组合码,由十七位数字本体码和一位校验码组成。排列顺序从左至右依次为:六位数字地址码,八位数字出生日期码,三位数字顺序码和一位数字校验码。具体数据特征和约束如下:
l 地址码:编码对象常住户口所在县级(市、旗、区)行政区划代码,按 GB/T2260 标准执行。
l  出生日期码:编码对象出生的年、月、日,按 GB/T7408 标准执行,年、月、日之间不用分隔符。
l 顺序码:在同一地址码所标识的区域范围内,对同年、同月、同日出生的人编定的顺序号,其中奇数分配给男性,偶数分配给女性。
l 校验码:根据前面十七位数字码,按照 ISO 7064:1983.MOD 11-2 校验码计算出来的检验码。
模拟生成身份证编码的 SPL 脚本如下:

A1:导入本地 txt 文本文件,其中包含“行政区划代码”数据。
A2-A4:随机生成“行政区划代码”数据表范围内的数据位置序号。并按照位置序号获取对应的所在区域。
A5:随机取得三位数字组成的顺序码字符串,偶数代表女,奇数代表男。
A6-A7:按照标准编码规则和计算公式,设置计算系数和校验码。
A8:字符串拼接组合身份证号码的前三组的字符串,即:地址码 + 出生日期码 + 顺序码。
A9-A11:按照身份证号码的标准计算公式,拆分字符串系数和校验码,计算出对应的一组校验码。
A12:返回身份证号,行政区域代码 + 出生日期 + 三位顺序码 + 一位校验码,特别地,校验码已包含 X 的情况,这个是根据校验码生成规则自动生成的一个身份证编码:
这个例子中利用了对字符串的拆分和组合,轻松实现了自动生成合法身份证号码的程序。
4)  序列与字符串的转换
这里补充一个小知识点,在处理字符串时,一方面可能需要将一个序列的值组合成一个字符串,另一方面又可能把一个字符串按照分隔符拆分成序列。SPL 中可以通过 split() 和 concat() 两个函数,很方便地实现序列和字串的相互转化,以便进一步计算。
其中,函数 s.split(d) 用来将字串 s 以分隔符 d 拆分成序列。函数 A.concat(d) 用来将序列 A 用分隔符 d 连接,拼接成字串,自动处理数据类型。两个函数中都可以通过不同的选项进行控制,例如 @c 选项,表示以逗号为分隔符,下面是几个例子:

A1:输入字符串。
A2:将 A1 的字符串以逗号为分隔符转化为序列。查看运行结果为:
 
A3:将 A2 的序列以逗号为分隔符转化为字符串。查看运行结果为:
 
    这个技巧在后面常规单数据表模拟的例子中有具体的应用。
5)  普通字段列自动扩展
在进行数据表模拟时,还可能需要模拟一些非必要的普通字段,这些字段可能记录了一些属性数值,也可能是空值,但在模拟数据查询时又是不可缺少的。SPL 提供了非常简便的方法,可以自动批量扩展出一些字段,快速填充:

A1:创建一个数据表,其中 empID 是主键,other1-other6 字段就是需要批量模拟生成的字段。
A2:循环扩展出 6 个随机取数函数,并转换为字符串。查看运行结果为:
A3:将模拟的数据插入到 A1 数据表中。其中,使用 ${A2} 动态进行宏运算,也就是将 A2 的字符串表达式作为宏进行动态计算,得到的随机数作为 insert() 函数中的参数。当然,这一步也可以手工直接一个一个写出来。查看运行结果为:

三、模拟生成单数据表
1、常规单数据表模拟
通常,我们做模拟测试或是功能验证,都是模拟一个单一的大宽数据表,需要的数据字段全部都在一个单数据表中,既方便数据过滤查询,也相对容易生成。现在我们就利用前面介绍的技巧,使用 SPL 生成一个大数据量的单数据表模拟数据,一个“员工登记表”的测试用数据。数据表结构如下:

模拟数据的生成规则:
l  按照数据表生成对应信息,包括:员工编号、姓名、身份证号码、性别、出生日期、所在区域、其它 1…其它 12 等信息。
l  各字段的生成规则:
n【员工编号】是依次产生(主键);
n【姓名】、【身份证号码】、【性别】、【出生日期】随机生成;
n【身份证号码】的隐藏条件是与【出生日期】和【性别】对应,即身份证号中的日期与【出生日期】相同,最右一位的顺序码与【性别】对应(奇数是男性,偶数是女性),
n  同时还需要保证【出生日期】模拟的员工年龄应在 18~65 岁之间;
n【所在区域】从固定范围数据表中随机获取;
n【其它 1】…【其它 12】模拟非必要条件的常规备用字段。
l  生成数据量:要求至少生成 1000 万条模拟数据。
以下是相应的 SPL 脚本:

A1-A2:从本地文件中导入人员登记表必要的字典表。包括:自动生成人员姓名需要的“姓氏.txt”、“男名.txt”、“女名.txt”和生成身份证需要的“行政区划代码.txt”数据。
A3:创建集文件“人员登记表.btx”,用于导出生成的模拟数据。
A4:创建临时人员登记表。
A5:设置模拟生成大数据的总量,这里按照要求设置 1 千万,B5 设置每次流式追加写入 btx 集文件的数据量,控制内存占用,避免内存溢出。
A6-A21:按照生成数据量的要求循环生成 1 千万的数据,并存入集文件“人员登记表.btx”中。其中使用的一些函数在上面都有介绍,这里就不再赘述。特别地,需要说一下单元格 B20,用 if 判断单元格 A4 临时表的数据长度是否大于等于 B5 设置的 10 万或是当前 A6 单元格的循环数值等于 A5 设置的阈值,如果条件符合为 true,在单元格 C20 使用 export@ab()流式写入追加 btx 集文件数据。最后,在单元格 C21 中使用 reset() 清空 A4 序表成员数据并保留创建的数据结构。
使用类似分页的流式追加数据,可以有效应对大数据量模拟数据生成的场景,可以避免在数据生成过程中占用大量的 JVM 内存,有效避免 JVM 内存溢出。
【注意事项:】
如果要查看生成的人员登记表的集文件数据,在集算器安装包中,提供了集文件浏览器,可以在集算器安装目录的 esProc\bin 路径下,运行,查看集文件。
使用 BTX 集文件浏览器打开有 1 千万条数据量的“人员登记表.btx”集文件,可以看到数据内容如下:

2、机构树型数据表模拟
上面介绍的生成大数据量常规单数据表模拟数据的例子,是数据从无到有的情况,还有一种情况是已有基础数据,需要基于基础数据按照规则生成新的模拟数据。
事实上:上述的场景中,已经利用了已有的“行政区划代码”数据字典表(包括:id 区域编码、区域名称),表中的内容如下图所示:

现在更进一步,假设需要基于“行政区划代码”数据表生成省、市、区县的三层组织机构的模拟数据。
这种结构的数据可以按照组织机构关系呈树型表示,如下图所示。数据表的其它字段与常规数据表字段生成原理相同。

对数据进行分析,可以发现行政区划代码的数据规律是:代码是定长的六位数,其中省的格式是 xx0000,市的格式是 xxxx00,区县的格式是 xxxxxx。基于这个规则,我们生成用于查询的模拟数据,并且包含对应层级的人口统计信息字段。
下面是相应的 SPL 脚本代码:

A1:导入“行政区划代码.txt”数据字典表并设置键。“行政区划代码.txt”中的 id 字段是唯一且不重复的主键,使用 keys() 对读取得到的序表 id 进行键设置,方便单元格 A2 和 A3 根据 id 主键查找数据。
C1:创建保存模拟数据的集文件。
A2:按照行政区域的机构编号规则,拆分出三层机构树,并增加相应的机构名称信息和随机的人口统计数,最终生成需要的包含了三层机构树型结构的模拟数据。具体做法是:根据上面分析行政区划代码数据的规律,使用 new() 将 id 字段拆分格式为 xx0000、xxxx00、xxxxxx 的三层机构数据,分别对应为:level1、level2、level3 字段。由于机构编码表的数据规则,这里的 level2 需要特别处理一下,当机构是直辖市时,省与市的机构编码是一样的,即 level1 与 level2 是相同的,因此要先使用 find() 查找 level2 值是否存在于“行政区划代码”表中,再使用 ifn() 判断返回非空成员的值,从而实现返回正确市 level2 的机构编号。其中,需要特别注意的地方是 keys() 与 find() 是需要一起配合使用。
返回的层级数据结果,如下图所示:

A3:将单元格 A2 中省、市的编码转为中文名称。使用 run() 函数可以针对每条记录计算表达式,然后返回记录本身。特别地,需要注意 run() 是直接改变原数据值。同样的,这里对第二层级“市”字段也进行了查找返回非空成员值的处理方法,最终运行的结果如下:

A4:将模拟生成的数据导出为“区域统计表.btx”集文件。
同样地,可以使用集文件浏览器打开该二进制数据文件查看数据:

四、补齐 / 补全固定数据
除了直接生成模拟数据,或者基于一些基础数据生成补充模拟数据,在业务中还可能需要在已有历史数据的基础上,针对存在的数据缺失情况,生成模拟数据进行补充。下面就介绍两种常见的固定数据补齐 / 补全的场景,从而应对数据缺失情况。
1、补齐 / 补全固定分组数据
产品完工记录存储在“building”数据表中,其中 YEAR 为完工时间(数据类型:字符型),格式为“年份 上半年 下半年”。现在要指定起止年份,统计出各类产品每半年的完工数量。固定数据信息如下:

“building”数据表中的年份不连续,统计时就需要进行额外的判断和处理。下面的 SPL 脚本在处理此类数据时就相对简单很多:

A1-D1:导入“building.txt”数据表,创建保存数据结果的 building.btx 集文件
A2:创建临时二维表。
A3:按照起、止时间段创建新的完整的年份统计表。
A4-B4:将 building 表按照 TYPE 分组,并循环处理每组数据。在单元格 B4 中使用 insert() 实现 A3 与 A4 的数据补齐,其中参数“0:A3”表示在 A2 二维表末尾的位置追加 A3 长度的多条记录数据,并将补齐 / 补全数据插入到单元格 A2 的二维表中。
A5:将 A2 数据结果导出到 building.btx 集文件中。查看集文件补齐数据结果如下:

2、补齐 / 补全固定分组并转置
       员工的出差记录存储在“OnBusiness”数据表中,包含:Date 和 ID_user 字段。而用户信息数据存储在“User”表中,包含:ID 和 Name 字段。需要实现在指定的时间段,按顺序列出每周每个员工是否出差状态信息。
这个场景的特殊之处是需要每个 User 员工对应占一列。OnBusiness 和 User 的部分数据,如下:

 
如果当起止日期是 2015-05-31、2015-06-28,则期望数据的结果,如下:

SPL 脚本如下所示:

A1:简单关联过滤查询区间范围内的数据。
C1:创建保存生成结果数据的 NewOnBusiness.btx 集文件。
A2:对单元格 A1 中关联过滤后的数据按 ID_User 分组。
A3:按照区间构造二维表的。每周 Week 占 1 行(间断时自动补齐数据),User1-User5 每个员工各占一列,默认初值都是“No”标识状态。
A4:通过多次调用 run() 函数循环 A2 数据并确定行记录后,再使用 A.field() 函数实现确定需要修改的列位置,最终将单元格 A3 构造的二维表对应的位置数据修改为员工出差状态为“Yes”状态值。
查看集文件补齐并转置的数据结果,如下:

上述运算的结果与预期数据结果一致,实现了对数据的补齐和转置。

正文完
 0