文档章节

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

atearsan
 atearsan
发布于 2013/12/24 16:34
字数 1078
阅读 18582
收藏 24

一、新增

    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)。上面的写法已经可以满足需要了……

INSERT INTO table(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, int conflictAlgorithm)

部分参数介绍(忽略参数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 "};

好了,结合方法 insertWithOnConflict的源码一起就明白了,当你指定了该参数,最终得到的SQL是 "INSERT OR REPLACE INTO table(column1, column2...) VALUES(val1, val2...)" 这样的格式……

这样就解决了最开始说的需求产生场景中遇到了的性能瓶颈。


分享出来,希望对有需要的人有帮助。

© 著作权归作者所有

共有 人打赏支持
上一篇: 工作碎片记录
下一篇: android权限大全
atearsan
粉丝 28
博文 27
码字总数 8782
作品 0
深圳
程序员
私信 提问
Hibernate-Sqlite尝试

今天在网上下载了Hibernate-Sqlite项目进行了尝试,为了帮助以后复习,记到这里了 一、项目结构及引用包 2.主要代码 其实整个项目最主要的就是定义了Sqlite方言 SQLiteDialect.java package...

落落的月
2012/01/13
0
0
EXCEL导入导出工具DataPieV3.7更新,大幅提升EXCEL导入ACCESS的效率

DataPie 托管地址:https://github.com/yfl8910/DataPie 功能:支持SQL server、SQLite、ACCESS数据库的导入、导出、存储过程调用,支持EXCEL2007、EXCEL2003、ACCESS2007、 CSV文件导入数据...

不锈的阳光
2014/06/03
2.7K
6
SQLite学习手册(临时文件)

一、简介: 尽管SQLite的数据库是由单一文件构成,然而事实上在SQLite运行时却存在着一些隐含的临时文件,这些临时文件是出于不同的目的而存在的,对于开发者而言,它们是透明的,因此在开发...

涩女郎
2015/08/26
0
0
Android 小项目之--SQLite 使用法门

每个应用程序都要使用数据,Android应用程序也不例外,Android使用开源的、与操作系统无关的SQL数据库 --大名鼎鼎的SQLite。SQLite是一款轻量级数据库,它的设计目的是嵌入式,而且它占用的资...

mayi
2011/11/14
0
2
ToughRADIUS 0.9.6 发布,Radius 服务软件

ToughRADIUS 0.9.6 版本发布了,代码下载地址:ToughRADIUS 0.9.6 Release ToughRADIUS是一个开源,免费,易用的Radius服务软件。 ToughRADIUS支持标准RADIUS协议,提供完整的AAA实现。支持灵...

jamiesun
2015/03/02
939
1

没有更多内容

加载失败,请刷新页面

加载更多

腾讯面试:一条SQL语句执行得很慢的原因有哪些?

说实话,这个问题可以涉及到 MySQL 的很多核心知识,可以扯出一大堆,就像要考你计算机网络的知识时,问你“输入URL回车之后,究竟发生了什么”一样,看看你能说出多少了。 之前腾讯面试的实...

java菜分享
17分钟前
2
0
Java 基本功 之 CAS

本文首发于个人公众号《andyqian》, 期待你的关注! 前言 在Java并发编程中,我们经常使用锁对竞争资源予以并发控制,以解决资源竞争的问题。但无论是使用 Lock 还是 Synchronized,随着锁机...

andyqian
21分钟前
3
0
信号量与条件变量的区别

注意信号量与条件变量的区别 信号量内容可见:http://www.cnblogs.com/charlesblc/p/6142868.html 信号量、共享内存,以及消息队列等System V IPC三剑客主要关注进程间通信; 而条件变量、互...

shzwork
32分钟前
0
0
在VirtualBox 6.0中安装fedora 30

操作系统安装完毕后首先进行更新。 sudo dnf update 重启虚拟机后,安装VirtualBox依赖的软件包。 sudo dnf install kernel-headers kernel-devel dkms gcc 最后,安装“增强功能”。...

gugudu
40分钟前
0
0
861. Score After Flipping Matrix

为了获得最大值,我们必须保证每一行列下标小的1尽可能的多(最高位的1尽可能多)。 首先,考虑我们可以进行的操作有 翻转列,进行列操作 翻转行,进行行操作 通过行操作 我们总是可以使得第...

reter
41分钟前
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部