【发布时间】:2016-10-24 11:27:09
【问题描述】:
场景:
我在我的 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 对象将包含 getter 方法以及另一个用于从外键获取 SecondItem 对象的方法:
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());
}
我怀疑这种方法有问题,因为它需要在 Table2 中搜索 Table1 中的每一行 - 我认为它效率低下。
一个想法:
我正在考虑使用另一种方法,但是我不知道它是否比我当前的解决方案更好,或者它是否是实现我想要的“正确”方法。我的意思是我不确定是否有一种方法可以稍微修改我当前的解决方案以显着提高性能。不过,我的想法是提高从数据库读取数据的速度。
当应用第一次加载时,会从 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 数据库中读取的速度,我必须在这些表中查看第一个表的每一行以产生所需的结果。这需要很长时间,而且效率低下,但我不确定是否有办法调整我当前的方法以大大提高读取速度。我认为我应该在应用程序首次运行时“加载”数据,方法是将各个表中的数据重新组织到一个表中。
所以我要问,这两种方法中哪一种更好(主要是关于性能)?有没有办法可以调整我当前的方法,或者我做错了什么?最后,如果有比我已经提到的两种方法更好的方法来做到这一点,它是什么以及我将如何实现它?
【问题讨论】:
-
对于那些投票结束和/或否决这个问题的人,我不认为这太宽泛了。我正在寻求实现上述目标的最佳方法,这可以通过修改第一种方法、使用第二种方法或其他方法来实现。我知道可能没有一个明确的答案,因为人们可能对此有不同的答案,但我还应该问什么是实现某事的最佳方法?我已在我的问题中添加了详细信息,但我不同意“这种格式的好答案会太长”。
-
我认为有些用户可能认为这“过于宽泛”,因为您仅使用“table1”和“table2”来解释您的情况。如果我们不知道每张桌子有什么,我们无法建议这是否是一个明智的想法。你可以给我们举一些例子。您可以使用虚拟数据作为示例,但实体应该是相同或相似的类别。是否应该合并两个表是一个棘手的情况。您不想一次又一次地添加列等等。
-
@Sometowngeek 我明白你的意思,但我也提到这些只是作为一个例子,我的实际数据包含大约 50 个表,列更多,通常大约 1000 行。我认为数据到底是什么并不重要,但它有多少,它是如何组织的,以及我正在用它做什么,因为我的问题不涉及数据,而是我如何处理它。但是,如果有帮助,请告诉我,我会更新我的答案。希望我没有听起来太防御或任何东西;)
-
在我的脑海中,我认为最快的方法是通过 SQL 查询将数据转储到 Excel 电子表格等输出文件中,然后创建一个新表。之后,将输出文件数据导入到新表中。不过,请确保在删除旧表之前验证数据是否正确。如果两个表通过 FK 链接在一起,这很容易做到。
-
显然,我的评论已经在@danail-alexiev 第一点。你应该听从他的建议。
标签: android database performance android-sqlite database-performance