批量删除用户,传过来的是一个integer[] ids,长度0~100万不定,当数据量大的时候删除太慢或者删除不了,现在想每次删1000条,时间长点可以接受,想知道怎么每次从ids取1000条,或者其他有更好的方案也可以,删除用户需要删除关联的东西太多,不能直接用sql in来删
参考如下示例代码:
package com.example.test;
import java.util.Arrays;
public class Test {
public static void main(String[] args) {
int maxRange = 2600;
int[] ids = new int[maxRange];
for (int i = 0; i < maxRange; i++) {
ids[i] = i;
}
int PER_QUERY_COUNT = 1000; //每次查询数量
int from = 0; //起始索引
int to = PER_QUERY_COUNT; //结束索引
for (int j = 0; j < ids.length; j += PER_QUERY_COUNT) {
//将一个原始的数组original,从小标from开始复制,复制到小标to,生成一个新的数组。
// 注意这里包括下标from,不包括下标to。
System.out.println("from=" + from + ",to=" + to);
int[] temp = Arrays.copyOfRange(ids, from, to);
for (int id : temp) {
System.out.print(id + ",");
}
from += PER_QUERY_COUNT;
to += PER_QUERY_COUNT;
if (to > ids.length) {
to = ids.length;
}
System.out.println("\r");
}
}
}
System.arraycopy
什么数据库?根据区间删除呢?
System.arraycopy就可以了
可以用SQL语句的exists或者not exists来进行删除,这个效率比in或者not in快,数据库里面去操作更方便一点。
批量删除目前我知道的只能用in,
一般是在service层把addsql拼好,
然后在持久层用类似Delete from 'table' where id in (addsql),
至于in()这里能放多少条,要看数据库了。
据说oracle 1000 条,sqlserver 更多
下面的代码是把数组分页处理,为了符合sql in()里的要求,每1000个id,变成用‘,’分开的字符串赋给addsql。
需要注意:
下面是service层的代码。
PAGESIZE可以自己根据具体情况调整
ps:我一般都用list,几乎没有用过数组。list的底层也是数组。我觉得list能满足更多的需求,以后还是多用list吧
private static final int PAGESIZE = 1000;//可调
public void delete(Integer[] ids) {
Dao dao = new Dao();//这里声明dao
if (ids != null && ids.length > 0) { //判空
int num = 1; //页码
int index = 0; //当前是哪一条
while (ids.length > index) {
if (ids.length > num * PAGESIZE) {
Integer[] idstemp = Arrays.copyOfRange(ids, (num-1) * PAGESIZE, num * PAGESIZE);//截取1000条
String addsql = ArrayToString(idstemp);//把1000个数字转换用“,” 分隔的字符串 比如:1,5,65,897。
dao.DeleteInfoById(appsql);//把上面的字符串传到dao层 ,然后Delete from 'table' where id in (addsql) 这里是1000个。可以自己调
index = num* PAGESIZE;
num++;//下一页
} else {
Integer[] idstemp = Arrays.copyOfRange(ids, (num-1) * PAGESIZE, ids.length);//当最后一页的数量大于剩余数量,取剩余数量
String appsql = ArrayToString(idstemp);//同上
dao.DeleteInfoById(appsql);//同上
index = ids.length;//最后一条
}
}
}
}
private String ArrayToString(Integer[] idstemp) { //把数组中的数字用‘,’隔开,返回字符串 比如:456,45646,45646,77543
if (idstemp != null && idstemp.length > 0) {
String str = "";
for (int i : idstemp) {
str = str + i + ",";
}
return str.substring(0, str.length() - 1);
}
return "";
}
声明一个ArrayList al;
for(int i ;i<ids.length;i++){
if(i % 1000 != 0){
将ids中的第i数据放入al;
}else{
将al的数据在数据库中处理
初始化al.
}
}
如果al不空,则 将al的数据在数据库中处理
这个问题真难,使用数组处理这么大的数据量,根本就是想都不用想的嘛,;使用数据库的时候,直接根据你传入的id获取其他主键标识去删就行了,,分不分批都是由你控制的;
这样是可行的
package com.myk.utils.test;
import java.time.Clock;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class ArrayUtils {
public static void main(String[] args) {
int arraySize = 1000000;
int splitSize = 1000;
Integer[] ids = new Integer[arraySize];
long time1 = Clock.systemUTC().millis();
for(int i = 0 ; i < arraySize ; i++) {
ids[i] = i;
}
batchRemoveArray(ids, splitSize);
long time2 = Clock.systemUTC().millis();
System.err.println("程序运行时间:"+(time2-time1));//程序运行时间在 15ms 左右
}
/**
* 批量移除数组数据
* @param integerArray:需要操作的数组
* @param removeSize:一次移除多少
*/
private static void batchRemoveArray(Integer[] integerArray, int removeSize) {
List<Integer> idsList = new ArrayList<>(Arrays.asList(integerArray));//将数组转换为list列表
//分块
int listSize = idsList.size();
int forSize = listSize/removeSize;//至少要循环几次
int forSizeClind = listSize%removeSize;//剩下的长度(不为0,则说明还有值,多加一次循环)
int xunhuancishu = forSize;
if(forSizeClind != 0) {//判断应该循环多少次
xunhuancishu += 1;
}
int currentSize = 0;
//时间复杂度O(xunhuancishu)
//平均运行时间为 1ms 左右
long time4 = Clock.systemUTC().millis();
for(int i = 0 ; i < xunhuancishu ; i++) {
if(i < forSize) {
idsList = idsList.subList(removeSize, listSize - currentSize);
currentSize += removeSize;
}else {
idsList = idsList.subList(forSizeClind, forSizeClind+1);//只要进来这里说明已经结束了
}
// System.out.println(idsList.size());//打印耗时在 10ms 左右
}
long time5 = Clock.systemUTC().millis();
System.err.println(time5 - time4);
}
}