Android: Distinct and GroupBy in ContentResolver
Since no one came to answer I'm just going to tell how I solved this. Basically I would create custom URI for each case and pass the criteria in selection
parameter. Then inside ContentProvider#query
I would identify the case and construct raw query based on table name and selection parameter.
Here's quick example:
switch (URI_MATCHER.match(uri)) {
case TYPES:
table = TYPES_TABLE;
break;
case TYPES_DISTINCT:
return db.rawQuery("SELECT DISTINCT type FROM types", null);
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
return db.query(table, null, selection, selectionArgs, null, null, null);
In your overridden ContentProvider
query method have a specific URI mapping to using distinct.
Then use SQLiteQueryBuilder
and call the setDistinct(boolean)
method.
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder)
{
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
boolean useDistinct = false;
switch (sUriMatcher.match(uri))
{
case YOUR_URI_DISTINCT:
useDistinct = true;
case YOUR_URI:
qb.setTables(YOUR_TABLE_NAME);
qb.setProjectionMap(sYourProjectionMap);
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
// If no sort order is specified use the default
String orderBy;
if (TextUtils.isEmpty(sortOrder))
{
orderBy = DEFAULT_SORT_ORDER;
}
else
{
orderBy = sortOrder;
}
// Get the database and run the query
SQLiteDatabase db = mDBHelper.getReadableDatabase();
// THIS IS THE IMPORTANT PART!
qb.setDistinct(useDistinct);
Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, orderBy);
if (c != null)
{
// Tell the cursor what uri to watch, so it knows when its source data changes
c.setNotificationUri(getContext().getContentResolver(), uri);
}
return c;
}
You can do nice hack when querying contentResolver, use:
String selection = Models.SOMETHING + "=" + something + ") GROUP BY (" + Models.TYPE;
If you want to use DISTINCT with SELECT more then one column, You need to use GROUP BY.
Mini Hack over ContentResolver.query for use this:
Uri uri = Uri.parse("content://sms/inbox");
Cursor c = getContentResolver().query(uri,
new String[]{"DISTINCT address","body"}, //DISTINCT
"address IS NOT NULL) GROUP BY (address", //GROUP BY
null, null);
if(c.moveToFirst()){
do{
Log.v("from", "\""+c.getString(c.getColumnIndex("address"))+"\"");
Log.v("text", "\""+c.getString(c.getColumnIndex("body"))+"\"");
} while(c.moveToNext());
}
This code select one last sms for each of senders from device inbox.
Note: before GROUP BY we always need to write at least one condition.
Result SQL query string inside ContentResolver.query method will:
SELECT DISTINCT address, body FROM sms WHERE (type=1) AND (address IS NOT NULL) GROUP BY (address)