情境:
我在我的Android应用程序中使用了一个相当大的SQLite数据库(大约20 MB),它包含大约50个表。
这些表中的大多数都是通过外键链接的,而且很多时候,我需要一次从两个或多个表中检索信息。举例说明:
表1 :
Id | Name | Attribute1 | Attribute2 | ForeignKey
1 | "Me" | SomeValue | AnotherVal | 49
2 | "A" | ... | ... | 50
3 | "B" | | | 49
表2 :
Id | Attribute3 | Attribute4 | Attribute5
49 | ThirdVal | FourthVal | FifthVal
50 | ... | ... | ...
有时候,有两个以上的方式以这种方式链接在一起。几乎在所有的时间里,列数多于上面列出的列数,通常大约有1000行。
我的目标是将数据库中的一些属性显示为RecyclerView
中的项目,但我需要使用这两个表来检索这些属性。
我的方法:
目前,我正在使用android-sqlite-asset-helper库将此数据库(.db
扩展名)从assets
文件夹复制到应用中。当我记录这次复制的时间时,它在732毫秒内完成,没问题。
但是,当我想使用第一个表中的外键从两个表中检索数据时,需要的时间太长。当我测试它时,花了大约11.47 秒,我想加快速度。
我检索数据的方式是我读取第一个表中的每一行,并将其放入一个对象中:
public static ArrayList<FirstItem> retrieveFirstItemList(Context context) {
Cursor cursor = new DbHelper(context).getReadableDatabase()
.query(DbHelper.TABLE_NAME, null, null, null, null, null, null);
ArrayList<FirstItem> arrayList = new ArrayList<>();
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
// I read all the values from each column and put them into variables
arrayList.add(new FirstItem(id, name, attribute1, attribute2, foreignKey));
cursor.moveToNext();
}
cursor.close();
return arrayList;
}
FirstItem
对象除了用于从外键获取SecondItem
对象的另一个方法外,还包含getter方法:
public SecondItem getSecondItem(Context context) {
Cursor cursor = new SecondDbHelper(context).getReadableDatabase().query(
SecondDbHelper.TABLE_NAME,
null,
SecondDbHelper.COL_ID + "=?",
new String[] {String.valueOf(mForeignKey)},
null, null, null);
cursor.moveToFirst();
SecondItem secondItem = new SecondItem(mForeignKey, attribute3, attribute4, attribute5);
cursor.close();
return secondItem;
}
当我将两个表中的值打印到logcat中时(我现在决定不使用任何UI,测试数据库性能),我使用这样的东西:
for (FirstItem firstItem : DBUtils.retrieveFirstItemList(this)) {
Log.d("First item id", firstItem.getId());
Log.d("Second item attr4", firstItem.getSecondItem(this).getAttribute4());
}
我怀疑这个方法有问题,因为它需要在 Table1 中的每一行搜索 Table2 - 我认为它效率低下。
一个想法:
我正在考虑使用另一种方法,但我不知道它是否比我目前的解决方案更好,或者它是否是实现我想要的“正确”方法。我的意思是,我不确定是否有一种方法可以稍微修改我当前的解决方案以显着提高性能。不过,我的想法是提高从数据库中读取数据的速度。
当应用程序第一次加载时,将读取来自SQLite数据库的各个表的数据,然后将其放入应用程序中的一个SQLite数据库中。当应用程序第一次运行并且每次更新数据库中的表时,都会发生此过程。我知道这会导致不同行之间的数据重复,但这是我看到的唯一方法,可以避免我必须搜索多个表来生成项目列表。
// read values from SQLite database and put them in arrays
ContentValues cv = new ContentValues();
// put values into variables
cv.put(COL_ID, id);
...
db.insert(TABLE_NAME, null, values);
由于这个过程也需要很长时间(因为有多行),我有点担心这不是最好的想法,但是我读到了一些Stack Overflow答案中的事务,这会增加写入速度。换句话说,我会适当地使用db.beginTransaction();
,db.setTransactionSuccessful();
和db.endTransaction();
来提高将数据重写到新的SQLite数据库时的性能。
所以新表看起来像这样:
Id | Name | Attribute1 | Attribute2 | Attribute3 | Attribute4 | Attribute5
1 | "Me" | SomeValue | AnotherVal | ThirdVal | FourthVal | FifthVal
2 | "A" | ... | ... | ... | ... | ...
3 | "B" | SomeValue | AnotherVal | ThirdVal | FourthVal | FifthVal
这意味着虽然表中会有更多列,但我可以避免在第一个表中为每一行搜索多个表,并且数据也可以更容易访问(用于过滤和类似的事情) 。大多数'加载'将在开始时完成,并希望加快交易方法。
概述:
总而言之,我想加快从具有多个表的SQLite数据库中读取数据,我必须在第一个表的每一行中查看这些表,以便产生所需的结果。这需要很长时间,并且效率低下,但我不确定是否有一种方法可以调整当前方法以大大提高读取速度。我想我应该在首次运行应用程序时“加载”数据,方法是将各个表中的数据重新组织到一个表中。
所以我问,两种方法中哪一种更好(主要是关于性能)?有没有办法可以调整我当前的方法,还是我做错了什么?最后,如果有一种比我已经提到过的两种方法更好的方法,那么它是什么以及我将如何实施呢?
答案 0 :(得分:2)
你应该尝试一些事情:
总而言之,对索引/视图保持智能应该在大多数情况下都能做到。将此与热切/延迟加载相结合,您应该能够达到对您的表现感到满意的程度。
编辑:有关索引,观看次数和Android实施的信息
索引和视图不是同一问题的替代方案。它们有不同的特点和应用。
将索引应用于列时,可以加快搜索这些列的值。您可以将其视为线性搜索与树搜索比较。这加速了连接,因为数据库已经知道哪些行对应于所讨论的外键值。它们对简单的select语句也有好处,不仅仅是使用连接的语句,因为它们也加快了where子句标准的执行速度。然而,他们有一个捕获。索引会加快查询速度,但会降低插入,更新和删除操作的速度(因为索引也必须保持不变)。
视图只是预编译和存储的查询,其结果集可以像普通表一样查询。这里的好处是您不需要每次都编译和验证查询。
你不应该只局限于这两件事中的一件。它们不是相互排斥的,并且在组合时可以为您提供最佳结果。
就Android实施而言,没有太多事情可做。 SQLite支持开箱即用的索引和查询。你唯一要做的就是创造它们。最简单的方法是修改数据库创建脚本以包含CREATE INDEX
和CREATE VIEW
语句。您可以将表的创建与索引的创建结合起来,或者如果需要更新现有的模式,可以稍后手动添加。只需检查SQLite手册以获取相应的语法。
答案 1 :(得分:0)
也许试试这个:https://realm.io/products/java/我从不使用它,我对他们的表现一无所知。它可能是一种让你感兴趣的方式......或者不是;)