SQLite 大批量数据 新增 / 修改 提升效率的办法

一、新增

SQLite中的新增默认是单个事务控制的,一次新增就是一次数据库操作,一次事务。如果几千次for循环操作,必然存在效率问题。下面代码是通过事务控制的方式提升效率:

public void addList(List<GroupMember> listMember) {
     StringBuffer sbSQL = new StringBuffer();
     
     SQLiteDatabase db = super .getDatabase();
     db.beginTransaction();
     for ( int i = 0 ; i < listMember.size(); i++) {
         GroupMember groupMember = listMember.get(i);
         if (i == 0 ) {
             // 根据当前用户id和圈子id删除圈子成员
             del(groupMember.getUserId(), groupMember.getGroupId()); // 第一次新增的时候删除历史数据
         }
         
         if (i != 0 ) {
             sbSQL.delete( 0 , sbSQL.length());
         }
         sbSQL.append( " INSERT INTO " ).append(TABLE).append( " (user_id, group_id, member_id, role_id) VALUES" );
         sbSQL.append( " (" ).append(groupMember.getUserId())
             .append( "," ).append(groupMember.getGroupId())
             .append( "," ).append(groupMember.getMemberId())
             .append( "," ).append(groupMember.getRole())
             .append( ");" );
         db.execSQL(sbSQL.toString());
     }
     db.setTransactionSuccessful();
     db.endTransaction();
}

 尽量别用下面的SQL语法,在部分机型上面会报错。(小米、三星S3)。上面的写法已经可以满足需要了……

INSERTINTOtable(column1, column2) VALUES(val1, val2), (val1, val2)


二、批量修改

    需求的出现:比如存在N个聊天圈子,圈子中有N个成员。每次进入圈子的时候后台线程下载圈子成员最新数据,并更新数据库。这时候存在三个表:圈子,圈子-成员关系表,用户表。

    用户表是所有圈子的用户,保存的时候需要判断是否存在,存在就新增,否则修改部分数据。(因为用户带有详细资料,而圈子成员返回的只有名字、账号、头像三个字段,不能用上面的方法先删除所有数据,然后批量新增)

    SQL关键写法如下(重点是SQLiteDatabase.insertWithOnConflict的用法):

public void insertOrReplace(List<GroupMember> listMember) {
     SQLiteDatabase db = super .getDatabase();
     db.beginTransaction();
     for ( int i = 0 ; i < listMember.size(); i++) {
         BaseUserInfo baseUserInfo = listMember.get(i).getBaseUserInfo();
         ContentValues cv = new ContentValues();
         cv.put( "user_id" , baseUserInfo.getUserId());
         cv.put( "name" , baseUserInfo.getName());
         cv.put( "logo" , baseUserInfo.getLogo());
         
         // 生成的sql是 INSERT INTRO OR REPLACE INTO 这样的 (如果存在就替换存在的字段值. 存在的判断标准是主键冲突, 这里的主键是userId). 下面会介绍这个地方的方法
         db.insertWithOnConflict(TABLE, null , cv, SQLiteDatabase.CONFLICT_REPLACE);
     }
     db.setTransactionSuccessful();
     db.endTransaction();
}

关键的一个地方:

insertWithOnConflict(String table, String nullColumnHack, ContentValues initialValues, intconflictAlgorithm)

部分参数介绍(忽略参数table和initialValues了,这个大家都知道吧 - -):

1) nullColumnHack当values参数为空或者里面没有内容的时候,我们insert是会失败的(底层数据库不允许插入一个空行),为了防止这种情况,我们要在这里指定一个列名,到时候如果发现将要插入的行为空行时,就会将你指定的这个列名的值设为null,然后再向数据库中插入。(实际开发中一般设置为null就好。)

    比如:如果values为空,最后生成的SQL大概是"INSERT INTO table"这样的,那么这是一个错误的SQL,肯定插入失败。但是如果指定了nullColumnHack,最终会生成SQL"INSERT INTO TABLE("+nullColumnHack+")" VALUES (null)"; 这样会插入一行没数据的行,但是SQL不会报错了。

    实际android源码如下:

public long insertWithOnConflict(String table, String nullColumnHack, ContentValues initialValues, int conflictAlgorithm) {
     acquireReference();
     try {
         StringBuilder sql = new StringBuilder();
         sql.append( "INSERT" );
         sql.append(CONFLICT_VALUES[conflictAlgorithm]);     // 注意这里, 等下介绍
         sql.append( " INTO " );
         sql.append(table);
         sql.append( '(' );
 
         Object[] bindArgs = null ;
         int size = (initialValues != null && initialValues.size() > 0 ) ? initialValues.size() : 0 ;
         if (size > 0 ) {
             bindArgs = new Object[size];
             int i = 0 ;
             for (String colName : initialValues.keySet()) {
                 sql.append((i > 0 ) ? "," : "" );
                 sql.append(colName);
                 bindArgs[i++] = initialValues.get(colName);
             }
             sql.append( ')' );
             sql.append( " VALUES (" );
             for (i = 0 ; i < size; i++) {
                 sql.append((i > 0 ) ? ",?" : "?" );
             }
         } else {
             sql.append(nullColumnHack + ") VALUES (NULL" );
         }
         sql.append( ')' );
 
         SQLiteStatement statement = new SQLiteStatement( this , sql.toString(), bindArgs);
         try {
             return statement.executeInsert();
         } finally {
             statement.close();
         }
     } finally {
         releaseReference();
     }
}

2) conflictAlgorithm

该参数是一个int值,上面源码中也用到了(CONFLICT_VALUES[conflictAlgorithm]),那么CONFLICT_VALUES的值是什么?Android源码中如下:

public static final int CONFLICT_ROLLBACK = 1 ;
 
public static final int CONFLICT_ABORT = 2 ;
 
public static final int CONFLICT_FAIL = 3 ;
 
public static final int CONFLICT_IGNORE = 4 ;
 
public static final int CONFLICT_REPLACE = 5 ;
 
public static final int CONFLICT_NONE = 0 ;
 
private static final String[] CONFLICT_VALUES = new String[]{ "" , " OR ROLLBACK " , " OR ABORT " , " OR FAIL " , " OR IGNORE " , " OR REPLACE " };


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值