背景
需要模拟生产环境的数据分布来往测试数据批量插入数据,以做一些性能测试。一开始是在存储过程中使用临时表,即往临时表赋值符合几何分布规律的插入数量,然后遍历该临时表来insert。后面发现这种情况只能满足一个字段的分布,要想满足其它字段的数据分布,只能用另一个临时表来为已经插入的数据进行update。这样感觉很麻烦,所以就想有没有不依赖临时表的实现。
前提知识
几何分布(Geometric Distribution)的公式:
几何分布的分布图:
本站关于几何分布的学习笔记(个人觉得还可以):概率论的学习和整理8: 几何分布-CSDN博客
代码
版本一:未进行一定的封装
public void fenbuNoDependTable() {
HashMap<String, Integer> hashMapCntPerRecords = new HashMap<>();
hashMapCntPerRecords.put("44060000", 20);
hashMapCntPerRecords.put("44010000", 13);
ArrayList<String> fieldXOfTableBList = new ArrayList<>();
fieldXOfTableBList.add("1064312");
fieldXOfTableBList.add("1074312");
fieldXOfTableBList.add("1086431");
/*
* 应用场景描述:
* 1. 场景一:往表A批量插入数据,表A的字段X的值的分布要符合几何分布并且值要从另外一个表B的字段X来获取。(表B字段X的值的集合我称为数据池)
* 2. 场景二:若要先遍历一个临时表,该临时表存了表A的Y字段的分布及其数量,比如省机构和插入数量(插入数量也满足几何分布),则在遍历该临时表时,
* 且根据临时表的插入数量来fori循环时,也可用这个。
* 例子:
* | 表A的Y字段, 插入数量|
* | 44060000, 20 | =>> fori 循环20次 => 即该20条记录的字段X的值需满足几何分布。
* | 44010000, 13 | =>> fori 循环13次 => 即该13条记录的字段X的值需满足几何分布
* (提醒:Y字段已经满足几何分布了,插入数据20、13就已经是几何分布计算出来的值)
*
*
* */
// 几何分布的p
float p = 0.3f; // 最大数量占比(这个比例来自于字段X的各值的最大数量占总数量的比例,即'group by 字段X order by count(字段X) desc'的最大数量占总数量的比例)
int k = 1; // 几何分布的k次数,不能和cntCurRowOfDataPool一致,因为会出现k > cntCurRowOfDataPool的情况。这时候我们需要将cntCurRowOfDataPool重置为1,重新循环
int curRowOfDataPool = 1;// 当前数据池的第几个值。(对应表B的字段X)
int cntCurRowOfDataPool = 1; // 数据池当前值使用了多少次。(数据池的当前值已经往表A插入了多少条数据,即使用了多少次)
double pbtyGeometric; // 每k次的几何分布概率。(随k增大而递减)
double pbtyActual; // 实际概率 (cntCurRowOfDataPool除以总数)
for (Map.Entry<String, Integer> entry : hashMapCntPerRecords.entrySet()) {
// 这里每轮都要把这三个值重置为1(如果不是在循环内,就不用重置为1)
curRowOfDataPool = 1;
cntCurRowOfDataPool = 1;
k = 1;
for (int i = 1; i <= entry.getValue(); i++) {
System.out.println("-------进入--------");
pbtyGeometric = Math.pow(p, k - 1) * p;
pbtyActual = (double) cntCurRowOfDataPool / entry.getValue();
System.out.print("pbtyActual: " + pbtyActual);
System.out.println("; pbtyGeometric: " + pbtyGeometric);
if (pbtyActual > pbtyGeometric) {
System.out.println("超过本轮几何分布的概率, 即本次插入需要使用数据池下一个值");
// 使用数据池下一个值
curRowOfDataPool++;
// 使用数据池下一个值的同时,几何分布的k也要+1,此时下一轮几何分布概率会变化,减小
k++;
// 数据池该值使用次数重置为1
cntCurRowOfDataPool = 1;
// 如果curRowOfDataPool超过数据池的值的数量,则重置为1
if (curRowOfDataPool > fieldXOfTableBList.size()) {
curRowOfDataPool = 1;
}
} else {
// 如果没有超过几何分布的概率
cntCurRowOfDataPool++;
}
System.out.println("curRowOfDataPool: " + curRowOfDataPool);
System.out.println("机构:" + entry.getKey() + "; 第" + i + "次; " + "virtualGiftNo为" + fieldXOfTableBList.get(curRowOfDataPool - 1));
}
}
}
运行结果
-------进入--------
pbtyActual: 0.07692307692307693; pbtyGeometric: 0.30000001192092896
curRowOfDataPool: 1
机构:44010000; 第1次; fieldX的值为1064312
-------进入--------
pbtyActual: 0.15384615384615385; pbtyGeometric: 0.30000001192092896
curRowOfDataPool: 1
机构:44010000; 第2次; fieldX的值为1064312
-------进入--------
pbtyActual: 0.23076923076923078; pbtyGeometric: 0.30000001192092896
curRowOfDataPool: 1
机构:44010000; 第3次; fieldX的值为1064312
-------进入--------
pbtyActual: 0.3076923076923077; pbtyGeometric: 0.30000001192092896
超过本轮几何分布的概率, 即本次插入需要使用数据池下一个值
curRowOfDataPool: 2
机构:44010000; 第4次; fieldX的值为1074312
-------进入--------
pbtyActual: 0.07692307692307693; pbtyGeometric: 0.09000000715255752
curRowOfDataPool: 2
机构:44010000; 第5次; fieldX的值为1074312
-------进入--------
pbtyActual: 0.15384615384615385; pbtyGeometric: 0.09000000715255752
超过本轮几何分布的概率, 即本次插入需要使用数据池下一个值
curRowOfDataPool: 3
机构:44010000; 第6次; fieldX的值为1086431
-------进入--------
pbtyActual: 0.07692307692307693; pbtyGeometric: 0.027000003218650946
超过本轮几何分布的概率, 即本次插入需要使用数据池下一个值
curRowOfDataPool: 1
机构:44010000; 第7次; fieldX的值为1064312
-------进入--------
pbtyActual: 0.07692307692307693; pbtyGeometric: 0.008100001287460403
超过本轮几何分布的概率, 即本次插入需要使用数据池下一个值
curRowOfDataPool: 2
机构:44010000; 第8次; fieldX的值为1074312
-------进入--------
pbtyActual: 0.07692307692307693; pbtyGeometric: 0.002430000482797661
超过本轮几何分布的概率, 即本次插入需要使用数据池下一个值
curRowOfDataPool: 3
机构:44010000; 第9次; fieldX的值为1086431
-------进入--------
pbtyActual: 0.07692307692307693; pbtyGeometric: 7.290001738071614E-4
超过本轮几何分布的概率, 即本次插入需要使用数据池下一个值
curRowOfDataPool: 1
机构:44010000; 第10次; fieldX的值为1064312
-------进入--------
pbtyActual: 0.07692307692307693; pbtyGeometric: 2.187000608325077E-4
超过本轮几何分布的概率, 即本次插入需要使用数据池下一个值
curRowOfDataPool: 2
机构:44010000; 第11次; fieldX的值为1074312
-------进入--------
pbtyActual: 0.07692307692307693; pbtyGeometric: 6.56100208568602E-5
超过本轮几何分布的概率, 即本次插入需要使用数据池下一个值
curRowOfDataPool: 3
机构:44010000; 第12次; fieldX的值为1086431
-------进入--------
pbtyActual: 0.07692307692307693; pbtyGeometric: 1.9683007039190456E-5
超过本轮几何分布的概率, 即本次插入需要使用数据池下一个值
curRowOfDataPool: 1
机构:44010000; 第13次; fieldX的值为1064312
-------进入--------
pbtyActual: 0.05; pbtyGeometric: 0.30000001192092896
curRowOfDataPool: 1
机构:44060000; 第1次; fieldX的值为1064312
-------进入--------
pbtyActual: 0.1; pbtyGeometric: 0.30000001192092896
curRowOfDataPool: 1
机构:44060000; 第2次; fieldX的值为1064312
-------进入--------
pbtyActual: 0.15; pbtyGeometric: 0.30000001192092896
curRowOfDataPool: 1
机构:44060000; 第3次; fieldX的值为1064312
-------进入--------
pbtyActual: 0.2; pbtyGeometric: 0.30000001192092896
curRowOfDataPool: 1
机构:44060000; 第4次; fieldX的值为1064312
-------进入--------
pbtyActual: 0.25; pbtyGeometric: 0.30000001192092896
curRowOfDataPool: 1
机构:44060000; 第5次; fieldX的值为1064312
-------进入--------
pbtyActual: 0.3; pbtyGeometric: 0.30000001192092896
curRowOfDataPool: 1
机构:44060000; 第6次; fieldX的值为1064312
-------进入--------
pbtyActual: 0.35; pbtyGeometric: 0.30000001192092896
超过本轮几何分布的概率, 即本次插入需要使用数据池下一个值
curRowOfDataPool: 2
机构:44060000; 第7次; fieldX的值为1074312
-------进入--------
pbtyActual: 0.05; pbtyGeometric: 0.09000000715255752
curRowOfDataPool: 2
机构:44060000; 第8次; fieldX的值为1074312
-------进入--------
pbtyActual: 0.1; pbtyGeometric: 0.09000000715255752
超过本轮几何分布的概率, 即本次插入需要使用数据池下一个值
curRowOfDataPool: 3
机构:44060000; 第9次; fieldX的值为1086431
-------进入--------
pbtyActual: 0.05; pbtyGeometric: 0.027000003218650946
超过本轮几何分布的概率, 即本次插入需要使用数据池下一个值
curRowOfDataPool: 1
机构:44060000; 第10次; fieldX的值为1064312
-------进入--------
pbtyActual: 0.05; pbtyGeometric: 0.008100001287460403
超过本轮几何分布的概率, 即本次插入需要使用数据池下一个值
curRowOfDataPool: 2
机构:44060000; 第11次; fieldX的值为1074312
-------进入--------
pbtyActual: 0.05; pbtyGeometric: 0.002430000482797661
超过本轮几何分布的概率, 即本次插入需要使用数据池下一个值
curRowOfDataPool: 3
机构:44060000; 第12次; fieldX的值为1086431
-------进入--------
pbtyActual: 0.05; pbtyGeometric: 7.290001738071614E-4
超过本轮几何分布的概率, 即本次插入需要使用数据池下一个值
curRowOfDataPool: 1
机构:44060000; 第13次; fieldX的值为1064312
-------进入--------
pbtyActual: 0.05; pbtyGeometric: 2.187000608325077E-4
超过本轮几何分布的概率, 即本次插入需要使用数据池下一个值
curRowOfDataPool: 2
机构:44060000; 第14次; fieldX的值为1074312
-------进入--------
pbtyActual: 0.05; pbtyGeometric: 6.56100208568602E-5
超过本轮几何分布的概率, 即本次插入需要使用数据池下一个值
curRowOfDataPool: 3
机构:44060000; 第15次; fieldX的值为1086431
-------进入--------
pbtyActual: 0.05; pbtyGeometric: 1.9683007039190456E-5
超过本轮几何分布的概率, 即本次插入需要使用数据池下一个值
curRowOfDataPool: 1
机构:44060000; 第16次; fieldX的值为1064312
-------进入--------
pbtyActual: 0.05; pbtyGeometric: 5.904902346396865E-6
超过本轮几何分布的概率, 即本次插入需要使用数据池下一个值
curRowOfDataPool: 2
机构:44060000; 第17次; fieldX的值为1074312
-------进入--------
pbtyActual: 0.05; pbtyGeometric: 1.7714707743109811E-6
超过本轮几何分布的概率, 即本次插入需要使用数据池下一个值
curRowOfDataPool: 3
机构:44060000; 第18次; fieldX的值为1086431
-------进入--------
pbtyActual: 0.05; pbtyGeometric: 5.314412534108716E-7
超过本轮几何分布的概率, 即本次插入需要使用数据池下一个值
curRowOfDataPool: 1
机构:44060000; 第19次; fieldX的值为1064312
-------进入--------
pbtyActual: 0.05; pbtyGeometric: 1.594323823585349E-7
超过本轮几何分布的概率, 即本次插入需要使用数据池下一个值
curRowOfDataPool: 2
机构:44060000; 第20次; fieldX的值为1074312
Process finished with exit code 0
版本二:仅返回符合几何分布的数据池的第几行
/*
* @Title: 几何分布核心
* @Description: 你给我传入总数和当前数量,我就能返回符合几何分布的数据池的index
* @param p: 几何分布的成功概率
* @param curRowOriTable: 原表的当前插入行数(次数)
* @param cntTotal: 需要插入数据的总数
* @param dataPoolList: 需要符合几何分布的数据池
* @return
*/
public static int geometricDisCore(float p, int curRowOriTable, int cntTotal, int dataPoolSize) {
int k = 1;
int curRowOfDataPool = 1;// 当前数据池的第几个值。
int cntCurRowOfDataPool = 1; // 数据池当前值已经使用的次数
double pbtyGeometric; // 第k次的几何分布概率。(随k增大而递减)
double pbtyActual; // 实际概率 (cntCurRowOfDataPool除以总数)
for (int i = 1; i <= curRowOriTable; i++) {
System.out.println("---------------------进入----------------------");
pbtyGeometric = Math.pow(p, k - 1) * p;
pbtyActual = (double) cntCurRowOfDataPool / cntTotal;
System.out.print("pbtyActual: " + pbtyActual);
System.out.println("; pbtyGeometric: " + pbtyGeometric);
if (pbtyActual > pbtyGeometric) {
System.out.println("超过本轮几何分布的概率, 即本次插入需要使用数据池下一个值"); // 这里其实本轮插入使用数据池当前值或下一个值都可以
// 使用数据池下一个值
curRowOfDataPool++;
// 几何分布的k要自增。(但k不能和curRowOfDataPool共同使用,因为curRowOfDataPool会重置为1)
k++;
// 如果curRowOfDataPool超过数据池的值的数量,则重置为1
if (curRowOfDataPool > dataPoolSize) {
curRowOfDataPool = 1;
}
// 数据池当前值已使用次数要重置为1,因为要计算数据池下一个值的次数。
cntCurRowOfDataPool = 1;
}
cntCurRowOfDataPool++;
System.out.println("curRowOfDataPool: " + curRowOfDataPool);
}
return curRowOfDataPool;
}
}
public void exec() {
int index;
ArrayList<Object> indexList = new ArrayList<>();
for (int i = 1; i <= 10; i++) {
index = geometricDisCore(0.3f, i, 10, 3);
indexList.add(index);
}
System.out.println(indexList);
}
/* 输出:[1, 1, 1, 2, 3, 1, 2, 3, 1, 2] */
版本二的oracle函数版本
create FUNCTION geometricDisForIndex(p in float, curRowOriTable in int, cntTotal in int, dataPoolSize in int)
RETURN int
AS
k int := 1; -- 几何分布的k
curRowOfDataPool int := 1; -- 当前数据池的第几个值。
cntCurRowOfDataPool int := 1; --
pbtyGeometric float;
pbtyActual float;
BEGIN
for i in 1.. curRowOriTable loop
pbtyGeometric := power(p, k-1) * p;
pbtyActual := cntCurRowOfDataPool / cntTotal;
--DBMS_OUTPUT.PUT_LINE('实际概率'|| TRUNC(pbtyActual,3)||'; 几何分布概率:'||TRUNC(pbtyGeometric,3));
IF pbtyActual > pbtyGeometric THEN
--DBMS_OUTPUT.PUT_LINE('超过本轮几何分布概率,此时使用数据池下一个值');
-- 使用数据池下一个值
curRowOfDataPool := curRowOfDataPool + 1;
-- 几何分布的k要自增。(但k不能和curRowOfDataPool共同使用,因为curRowOfDataPool会重置为1)
k := k + 1;
-- 如果curRowOfDataPool超过数据池的值的数量,则重置为1
IF curRowOfDataPool > dataPoolSize THEN
curRowOfDataPool := 1;
end if;
-- 数据池当前值已使用次数要重置为1,因为要计算数据池下一个值的次数。
cntCurRowOfDataPool := 1;
end if;
cntCurRowOfDataPool := cntCurRowOfDataPool + 1;
--DBMS_OUTPUT.PUT_LINE('curRowOfDataPool: '||curRowOfDataPool);
end loop;
return curRowOfDataPool;
END geometricDisForIndex;
/