我有一个(足球)游戏数据库,包含周期(例如第一个和第二个一半)的子表,事件(例如目标,警告)和位置(您先前的位置到和在比赛期间)。如何有效地嵌套SQLlite查询
要显示父游戏表,我使用的是CursorLoader,像这样适当的参数:
public Loader<Cursor> onCreateLoader(final int id, final Bundle args) {
...
if ((mGamesDB.isOpen()) && (id == GAMES_CURSOR_ID)) {
return createGamesCursorLoader();
}
return null;
}
private Loader<Cursor> createGamesCursorLoader() {
//Because we don't want to create a ContentProvider for now, we use the technique suggested here:
//https://stackoverflow.com/questions/18326954/how-to-read-an-sqlite-db-in-android-with-a-cursorloader
return new CursorLoader(getBaseContext(),null, GamesContract.Games.PROJECTION,
null, null, GamesContract.Games.ORDER_BY) {
@Override
public Cursor loadInBackground() {
if (mGamesDB.isOpen()) {
return mGamesDB.query(
GamesContract.Games.TABLE_NAME,
GamesContract.Games.PROJECTION,
null, null,
null, null,
GamesContract.Games.ORDER_BY
);
}
else return null;
}
};
}
这一切工作正常。但是,一旦我开始迭代游戏游标(当调用onLoadFinished时),我需要使用当前的GameID为Periods,Events和Locations创建子查询。所以,我做的:
private Game buildGameFromDB(final Cursor gameCursor) {
if (!mGamesDB.isOpen() || (gameCursor == null) || gameCursor.isClosed()) return null;
final WatchGame game = new WatchGame(gameCursor.getString(GamesContract.Games.COLUMN_ID_INDEX),
gameCursor.getLong(GamesContract.Games.COLUMN_ACTUAL_START_MILLIS_INDEX),
gameCursor.getLong(GamesContract.Games.COLUMN_ACTUAL_END_MILLIS_INDEX),
gameCursor.getInt(GamesContract.Games.COLUMN_HOME_TEAM_COLOR_INDEX),
gameCursor.getInt(GamesContract.Games.COLUMN_AWAY_TEAM_COLOR_INDEX),
gameCursor.getInt(GamesContract.Games.COLUMN_HOME_TEAM_SCORE_INDEX),
gameCursor.getInt(GamesContract.Games.COLUMN_AWAY_TEAM_SCORE_INDEX));
//FIXME: Ugly nested queries on the main UI thread
final String[] periodsWhereArgs = {game.getmGameID()};
final Cursor periodsCursor = mGamesDB.query(GamesContract.Periods.TABLE_NAME, GamesContract.Periods.PROJECTION,
GamesContract.Periods.WHERE, periodsWhereArgs,
null, null, GamesContract.Periods.ORDER_BY);
while (periodsCursor.moveToNext()) {
final Period period = new Period(
periodsCursor.getInt(GamesContract.Periods.COLUMN_PERIOD_NUM_INDEX),
periodsCursor.getLong(GamesContract.Periods.COLUMN_ACTUAL_START_MILLIS_INDEX),
periodsCursor.getLong(GamesContract.Periods.COLUMN_ACTUAL_END_MILLIS_INDEX),
periodsCursor.getFloat(GamesContract.Periods.COLUMN_START_BATTERY_PCT_INDEX),
periodsCursor.getFloat(GamesContract.Periods.COLUMN_END_BATTERY_PCT_INDEX),
periodsCursor.getString(GamesContract.Periods.COLUMN_GOOGLE_ACCOUNT_NAME_INDEX),
periodsCursor.getInt(GamesContract.Periods.COLUMN_NUM_LOCATIONS_INDEX),
periodsCursor.getInt(GamesContract.Periods.COLUMN_NUM_LOCATIONS_IN_FIT_INDEX),
periodsCursor.getInt(GamesContract.Periods.COLUMN_CALORIES_INDEX),
periodsCursor.getInt(GamesContract.Periods.COLUMN_STEPS_INDEX),
periodsCursor.getInt(GamesContract.Periods.COLUMN_DISTANCE_METRES_INDEX),
periodsCursor.getLong(GamesContract.Periods.COLUMN_WALKING_MILLIS_INDEX),
periodsCursor.getLong(GamesContract.Periods.COLUMN_RUNNING_MILLIS_INDEX),
periodsCursor.getLong(GamesContract.Periods.COLUMN_SPRINTING_MILLIS_INDEX)
);
game.addPeriod(period);
}
periodsCursor.close();
...
虽然游戏和期间的个数不会太大(也许100S),有可能是每场比赛的50个事件,和每场比赛2000点的位置。
我该如何更有效地做到这一点?发生在我的可能性是:
- 大型多连接查询,然后我不得不通过排序。我非常喜欢这种类型的SQL,假设SQLite将有效地处理它。我不喜欢这种情况,主要是因为时间段,事件,地点和子表,所以我实际上正在反规范化并造成巨大的混乱。
- 扩大我selectionArgs两个为周期,事件等须的10次或100的游戏的动态列表我有
- 不知何故改善我有效率和把它们转变成异步查询
任何意见或指针赞赏。
PS运行应用程序,然后查看日志会告诉你,如果任何索引是自动创建的,那么这些将成为创建永久索引的候选对象。 – MikeT
哇,谢谢你清楚,有用的答案。关于“许多小问题同样高效”的观点非常重要。我想我会以这种方式进行,然后做一些分析。 此外,重新索引:我会看看确保正确的索引正在创建或自己做。 – Opus1217