西安未央区网站建设,网站内如何@,做网站虚拟主机哪里有,做微网站需要什么简介#xff1a; 造一点模拟数据的方法
概述
造数据在一些奇怪的场合会被用到。一般我们是先有数据才有基于数据的应用场合#xff0c;但是反过来如果应用拿到另外一个场景#xff0c;没有数据功能是没有方法演示的。 一般较为真实的数据#xff0c;脱敏后就可以应用在功…简介 造一点模拟数据的方法
概述
造数据在一些奇怪的场合会被用到。一般我们是先有数据才有基于数据的应用场合但是反过来如果应用拿到另外一个场景没有数据功能是没有方法演示的。 一般较为真实的数据脱敏后就可以应用在功能测试和演示的场合。但是数据脱敏其实也满复杂脱敏过重数据就用不了了过低数据又泄漏了所以自己模拟一些数据似乎更安全。 我个人一般遇到的造数据场景有两个。第一是有合作伙伴或者同事咨询一个SQL处理数据的方法没有数据。第二就是有时候会有POC的一些场景没有提供真实模拟数据需要自己模拟。
分类
如果是单一的业务场景的数据模拟很多时候单表就可以满足了。但是要是模拟某个业务场景或者POC测试场景则要模拟一个业务系统中的相互关联的多张表。 造数据一般会都会有些用户需求会有明确的业务场景的描述。也会有一些其他要求例如表的记录数、行的存储、字段的生成规则、字段的值域、字段的枚举值还可能会给少量真实的数据。
2.1. 一个表 单独造一张表的数据可能非常简单比如我们日常测试一个函数测试一段SQL的JOIN逻辑。也可能非常复杂构造一个表也就相当于构造一个业务系统。
2.2. 一个业务系统 业务系统相对于单表来说只是表的数量增加了。而且因为业务系统的表间是存在主外键关系的所以需要先造代码表维度表然后再造业务表事实表。
方法
造模拟数据的方法分为两个阶段第一阶段是构造一个小表产生代码表维度表然后第二阶段利用笛卡尔积快速乘出需要的数据量。在这其中列的数据值填充可以使用随机函数生成。
3.1. 构造一个常量小表 Maxcompute最简单的造数据的方法是insert into values语句这一般也是我最常用的。在不支持这个语句之前的更早的版本使用的是union all的方法。如果不想实际写入数据到则可以使用from values 和 with 表达式。
示例1通过insert … values操作向特定分区内插入数据。 命令示例如下
--创建分区表srcp。
create table if not exists srcp (key string,value bigint) partitioned by (p string);--向分区表srcp添加分区。
alter table srcp add if not exists partition (pabc);--向表srcp的指定分区abc中插入数据。
insert into table srcp partition (pabc) values (a,1),(b,2),(c,3);--查询表srcp。
select * from srcp where pabc;--返回结果。
------------------------------------
| key | value | p |
------------------------------------
| a | 1 | abc |
| b | 2 | abc |
| c | 3 | abc |
------------------------------------
示例2通过values table操作插入数据。
命令示例如下
--创建分区表srcp。
create table if not exists srcp (key string,value bigint) partitioned by (p string);--向表srcp中插入数据。
insert into table srcp partition (p) select concat(a,b), length(a)length(b),20170102 from values (d,4),(e,5),(f,6) t(a,b);--查询表srcp。
select * from srcp where p20170102;--返回结果。
------------------------------------
| key | value | p |
------------------------------------
| d4 | 2 | 20170102 |
| e5 | 2 | 20170102 |
| f6 | 2 | 20170102 |
------------------------------------
values (…), (…) t(a, b)相当于定义了一个名为t列为a和b数据类型分别为STRING和BIGINT的表。列的类型需要从values列表中推导。 示例3from values或者union all组合的方式构造常量表。 命令示例如下
with t as (select 1 c union all select 2 c) select * from t;
--等价于如下语句。
select * from values (1), (2) t(c);--返回结果。
------------
| c |
------------
| 1 |
| 2 |
------------
以上例子来源于https://help.aliyun.com/document_detail/73778.html?spma2c4g.11186623.6.732.7e477b57ZhLOGj
3.2. 利用笛卡尔积构造大表 众所周知笛卡尔积的写法只能用在MAPJOIN提示的情况下。所以第一步构造出来的常量小表是可以使用MAPJOIN的。 命令示例如下 -- 1 构造一个常量表我这里用的有序数字方便使用where去取制定数量的记录数去乘笛卡尔积
create table za1 as
select c0 from values(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13),(14),(15)
,(16),(17),(18),(19),(20),(21),(22),(23),(24),(25),(26),(27),(28),(29),(30)
,(31),(32),(33),(34),(35),(36),(37),(38),(39),(40),(41),(42),(43),(44),(45)
,(46),(47),(48),(49),(50),(51),(52),(53),(54),(55),(56),(57),(58),(59),(60)
,(61),(62),(63)
t(c0);-- 2 使用常量表多次关联构造出需要的记录数[大家使用计算器大概算一下N的多少次方够用]
create table zb1 as
select *from(
-- 10*63*6339690
select /*mapjoin(t2,t3)*/1000000 row_number() over(partition by 1)-1 as c0from za1 t1 -- 63join za1 t2 -- 63join(select c0 from za1 limit 10)t3 -- 10
)t
;--3 第2步构造的表已经达到万级用这个表再构造的表记录数就可以轻松达到亿级
3.3. 利用随机值有序值填充列 数据种类从本质上可以分为2种序列值和枚举值。序列值就是有序的一个数列使用row_number()函数来实现在这个场景里主要定义为主键。枚举值就是少数的一些代码值数值、金额、代码分布在记录中这些枚举值主要使用随机函数来填充。其他情况目前个人还未遇到就不描述了。 命令示例如下
-- 1 有序值在这个例子中生成的数据是一个有序的从1000000-1036689的序列可以作为业务主外键使用
select /*mapjoin(t2,t3)*/1000000 row_number() over(partition by 1)-1 as c0from za1 t1 -- 63join za1 t2 -- 63join(select c0 from za1 limit 10)t3 -- 10
;-- 2 随机值/固定值在这个例子中c2列会生成一个相对均匀的1-1000的值
-- 随机函数生成的随机数是浮点值必须要转为bigint
select /*mapjoin(t2,t3)*/1000000 row_number() over(partition by 1)-1 as c0,1617120000 as c1,cast(round(rand()*999,0) as bigint)1 as c2from za1 t1 -- 63join za1 t2 -- 63join(select c0 from za1 limit 10)t3 -- 10
;
3.4. 不同的数据类型的构造 一般数据类型可以分为4种主键唯一值、字符串代表的枚举值、数值、日期时间。刚才的例子里面构造的都是数值唯一区别的是枚举值是数字而不是文本而且没有构造日期时间。那么如果确实需要该怎么实现。 时间可以构造成unixtime就可以转化为数值。文本类型的枚举值可以先构造代码表再构建好业务表后再关联出来一般业务系统存储的也是代码值而不是一个长字符串。 命令示例如下
-- 利用代码表转文本
with za as (
select * from values(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13),(14),(15)
,(16),(17),(18),(19),(20),(21),(22),(23),(24),(25),(26),(27),(28),(29),(30)
,(31),(32),(33),(34),(35),(36),(37),(38),(39),(40),(41),(42),(43),(44),(45)
,(46),(47),(48),(49),(50),(51),(52),(53),(54),(55),(56),(57),(58),(59),(60)
,(61),(62),(63)
t(c0)
)
,ta as (
select * from values (zhangsan,4),(lisi,5),(wangmazi,6) t(a,b))
select k,a,b,cfrom(
select 100 row_number() over(partition by 1)-1 as k,cast(round(rand()*3,0) as bigint)3 as cfrom za -- 63limit 3
)tb join ta on ta.btb.c
;
返回
k a b c
101 lisi 5 5
102 wangmazi 6 6
103 zhangsan 4 4-- 利用unixtimetamp转日期时间
with za as (
select * from values(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13),(14),(15)
,(16),(17),(18),(19),(20),(21),(22),(23),(24),(25),(26),(27),(28),(29),(30)
,(31),(32),(33),(34),(35),(36),(37),(38),(39),(40),(41),(42),(43),(44),(45)
,(46),(47),(48),(49),(50),(51),(52),(53),(54),(55),(56),(57),(58),(59),(60)
,(61),(62),(63)
t(c0)
)
select k
,from_unixtime(1617120000) as t
,from_unixtime(1617120000
3600000 * c ) -- 小时as b
,cfrom(
select 100 row_number() over(partition by 1)-1 as k,cast(round(rand()*3,0) as bigint)3 as cfrom za -- 63limit 3
)tb
;
返回
k t b c
100 2021-03-31 00:00:00 2021-03-31 03:00:00 3
101 2021-03-31 00:00:00 2021-03-31 05:00:00 5
102 2021-03-31 00:00:00 2021-03-31 06:00:00 6
实践
4.1. 实践案例 在前段时间经历的一个电信行业的POC项目客户最开始给了80行真实数据要求造十几亿左右的数据并给了一些非常特殊的数据要求。 原始数据和根据客户要求处理过程处理完后的数据特征的要求 记录数单表的记录数原始16亿处理后1.7亿 用户数1千4百万 设备数23万 单行记录大小原始数据行记录436KB处理完后是157KB 单用户记录数最小、最多、中位最小值是1最大值原始未3万处理后是2千4百中位数原始值是51处理后是4 如下表
时间类别记录数用户数用户记录数最小用户记录数最多用户记录数中位设备数单行KB9:00原始166848605914297500131973512312724369:00处理17481769413371656124414230860157
根据上述要求第一步是分析业务需求原始数据有61列但是真实参与数据计算的列只有10列。所以构造原始表只需要把这10列构造出来再把原始给的61列的记录的列选取1行关联上去即可。 分析原始数据结构选区参与计算的数据列
create table if not exists t_log10 (imei_tac int comment 用户设备ID1
,phone7 int comment 用户设备ID2
,imsi string comment 用户设备ID3
,msisdn string comment 用户设备ID4
,tac int comment 电信设备ID1
,cell_id int comment 电信设备ID2
,Procedure_Type int comment 业务类型
,Procedure_Start_Time bigint comment 业务开始时间unixtimestamp
,Procedure_status int comment 业务状态固定值1
,country_code int comment 国家码固定值-406 )
partitioned by (hh string);
电信业务中这个业务场景描述的是用户手机设备在电信运营商基站设备上注册的情况。这个业务计算使用的字段10个。有5个是用户设备维度相关分别是用户设备ID1-4和国家码有2个是电信设备维度相关分别是电信设备ID1-2。还有3个是用户设备与电信设备业务发生相关的分别是业务类型、业务状态、业务开始时间。 所以在做了需求分析后我认为我需要先构建一个用户设备维度表和电信基站设备维度表再根据这些维度表构建电信业务事实表业务表。 第一步构建电信基站维度代码表
drop table if exists t_tac_lacid;
create table if not exists t_tac_lacid (id bigint,tac bigint,lacid bigint);insert overwrite table t_tac_lacid
select /*mapjoin(t2)*/row_number() over(partition by 1)100000 as rn
,t1.c06001 as tac
,t2.c01201 as lacid
from (select row_number() over(partition by 1)-1 as c0 from zb1 limit 2300)t1
join (select row_number() over(partition by 1)-1 as c0 from zb1 limit 100)t2
;
-- 230000
在这个例子通过构建的zb1选区特定的记录数通过笛卡尔积乘出指定的记录数的结果集。因为两个ID要构建出唯一主键所以这里使用了row_number窗口函数。在构建主键的时候使用了100000这种方式来构建固定长度的ID。 第二步构建用户设备维度代码表。
drop table if exists t_user;
create table t_user (imei_tac bigint,phone7 bigint,imsi string ,msisdn string);insert overwrite table t_user
select rn as imei_tac
,cast(substr(to_char(rn),2,7) as bigint)1000000 as phone7
,substr(MD5(rn), 1,10) as imsi
,substr(MD5(rn),11,10) as msisdn
from(
select /*mapjoin(t2,t3,t4)*/row_number() over(partition by 1)10000000 as rn
from za1 t1
join za1 t2
join za1 t3
join (select c0 from za1 limit 58) t4
-- limit 100
)t;
-- 14502726
-- 63*63*63*58 14502726
在这个例子通过4次使用za1这个表构建了一个看起来很真实的记录数实际上造数据差几条没区别这里有点无聊。使用row_number窗口函数构建了业务主键并转化了几种形式MD5截取构建了不同的主键的样式。然后使用了随机函数构建了基站信息。这里面实际上把基站信息也做了计算这些特殊处理主要是为了构建最后的结果表。 最后一步就是构建结果表了因为前面我们还没有考虑中位数、极值和处理后结果的问题所以实际上最后的实现比较复杂太长了就不粘出来了有需要单独找我要吧。 满足特殊要求的方法是用户分段 1) 极值非常小的用户记录数满足用户极值[例如选500个用户 2) 中位数中位数一定是超过了一半以上的用户的记录数 3) 补充数除去极值与中位数剩下的用户 需要使用提示来改善性能因为造数据的原始表都非常小map阶段一般只有1个worker。所以必须要把map阶段的数据块输入切小把map和reduce的资源给大了。
set odps.sql.mapper.cpu200;
set odps.sql.mapper.memory8192;
set odps.sql.mapper.split.size4;
set odps.sql.reducer.cpu200;
set odps.sql.reducer.memory8192;
4.2. 总结 造数据场景大部分时候都比较简单但是也会遇到上述这种特殊的复杂情况。但是复杂的业务主要还是考验数据加工的能力怎么使用基础表生成复杂表还是关系数据库的关系模型的构建的过程。 单个数据表的构建首先需要先分析出业务中的维度和事实的部分再构建维度利用维度构建事实。
原文链接
本文为阿里云原创内容未经允许不得转载。