0

I have a table in my SQLite database:

Species:

_id  |  species  |  description 
---------------------------
  1  |  Aardvark |  Some description for Aardvark
  2  |  Aardwolf |  Some description for Aardwolf
  3  |  Caracal  |  Some description for Caracal

I receive a list of ids from the server and I want to display only the species corresponding to the ids I received.

Now I am aware of a few options:

1. The obvious and naive way of doing this is something along the lines of:

SQLiteDatabase db = this.openDatabase();
for (int id : idList) {
    Cursor cursorSpecies = db.query(true, TABLE_SPECIES, new String[] {COL_SPECIES_SPECIES},
            COL_ID + "=?", id, null, null, null, null);
    cursorSpecies.moveToNext();
    speciesList.add(cursorSpecies.getString(0));
    cursorSpecies.close();
}

This will execute way too many operations and I assume multiple small "disk" reads, which would be very slow.

2. Another option would be to use an SQLiteStatement but this only returns a single value, which doesn't work for my example and shouldn't really be used for queries anyway.

3. Yet another option would be to manually concatenate the conditions into a raw SQL query, something along the lines of:

SQLiteDatabase db = this.openDatabase();
String query = "SELECT * FROM " + TABLE_SPECIES + " WHERE ";
for (int id : idList) {
    query += COL_ID + "=" + id + " OR ";
}
// I am aware this will end in an " OR" but this is not the point of this example so please ignore it.
Cursor cursorSpecies  = db.rawQuery(query, null);
// Use the cursor and close it.

While this should work decently well, a very large query would probably break some query string length limit so this is not ideal either.

All of these examples would work to a certain extent but they all have pitfalls. Somehow I feel that I am missing a solution to this, hence the question:

What is the proper way of executing this type of query?

Thank you.

Community
  • 1
  • 1
Vlad Schnakovszki
  • 7,446
  • 5
  • 74
  • 105
  • insert `idList` into some temp table and join ... – Selvin Jun 25 '15 at 12:44
  • 1
    also `COL_ID = 1 OR COL_ID = 2 ... OR COL_ID = n` can simply be `COL_ID IN(1,2, ..., n)` – Selvin Jun 25 '15 at 12:50
  • 1
    I agree with laalto's answer, but I predict that when you measure your program, you will find that **1.** does not have any *noticeable* overhead. (Use a single transaction around all queries.) – CL. Jun 26 '15 at 11:47

1 Answers1

1

For the special case in the question, consider just WHERE id IN (x,y,z, ...).

To address the question in the title and not just the special case in the question body:

The Android SQLite API is not very versatile in this regard.

In raw sqlite3 C API one would do it with a single sqlite3_prepare*() call to obtain a sqlite3_statement and bind the arguments in place, sqlite3_step() to get row(s) and then reset the statement to reuse it with new argument bindings.

In Android API, the statement corresponds to a Cursor and stepping is equivalent of moving the cursor. The reset and rebind functionality is only available in SQLiteCursor as requery() and setSelectionArguments().

So try something along the following lines:

  1. Do a regular query with selection args.

  2. Assuming the default cursor factory, cast the resulting Cursor to a SQLiteCursor.

  3. Access the row(s) you need.

  4. Update the selection args with setSelectionArgs()

  5. requery()

  6. Goto 3 unless done

laalto
  • 137,703
  • 64
  • 254
  • 280
  • It's worth noting that `WHERE id IN (a1,a2,a3, ...)` is limited to 999 a's in SQLite. See: https://stackoverflow.com/a/15313495/5096103 – Cillian Myles Jan 09 '20 at 14:49