Ответ 1
Вы можете создать пользовательский Uri
, который, когда ваш UriMatcher
в вашем ContentProvider
получит его, вы можете вставить предложение group by, а затем выполнить необработанный sql непосредственно в базе данных.
Я делаю запрос в Android ContentProvider. Мне нужно предложение Group By. В Gingerbread и Honeycomb я делаю что-то подобное, чтобы одновременно искать номера телефонов и электронные письма:
(Фактическое предложение WHERE намного сложнее, поскольку оно включает проверки типов. Это упрощение, но оно дает тот же результат)
String request = Phone.NUMBER + " LIKE ? OR " + Email.DATA + " LIKE ?";
String[] params = new String["%test%", "%test%"];
Cursor cursor = getContentResolver().query(
Data.CONTENT_URI,
new String[] { Data._ID, Data.RAW_CONTACT_ID },
request + ") GROUP BY (" + Data.RAW_CONTACT_ID,
params, "lower(" + Data.DISPLAY_NAME + ") ASC");
Ввод ")" завершает предложение WHERE и позволяет вставлять предложение GROUP BY.
Однако, в Ice Cream Sandwich, похоже, что ContentProvider обнаруживает это и добавляет правильное число круглых скобок, чтобы предотвратить мою инъекцию. Любой другой способ сделать это в одном запросе курсора?
В настоящее время я удалил GROUP BY и добавил MatrixCursor для ограничения воздействия, но я предпочел бы иметь реальный курсор:
MatrixCursor result = new MatrixCursor(new String[] { Data._ID, Data.RAW_CONTACT_ID });
Set<Long> seen = new HashSet<Long>();
while (cursor.moveToNext()) {
long raw = cursor.getLong(1);
if (!seen.contains(raw)) {
seen.add(raw);
result.addRow(new Object[] {cursor.getLong(0), raw});
}
}
Вы можете создать пользовательский Uri
, который, когда ваш UriMatcher
в вашем ContentProvider
получит его, вы можете вставить предложение group by, а затем выполнить необработанный sql непосредственно в базе данных.
сначала извините мой НЕПРАВИЛЬНЫЙ английский! Я новичок в Java/Android, начал с 4.2.1 и сражался с этим почти через 2 дня, затем начал читать более подробную информацию о SQLiteQueryBuilder часть query - это то, что вы ищете;)
он имеет:
public Cursor query (SQLiteDatabase db, String[] projectionIn, String selection, String[] selectionArgs, String groupBy, String having, String sortOrder)
функция запроса "Content" поставщика контента дает вам:
query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder)
здесь вы можете обмануть, я отправлю вам свой код:
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
/* a String is a Object, so it can be null!*/
String groupBy = null;
String having = null;
switch (sUriMatcher.match(uri)) {
...
...
...
case EPISODES_NEXT:
groupBy = "ShowID";
queryBuilder.setTables(EpisodenTable.TableName);
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
Cursor c = queryBuilder.query(db, projection, selection, selectionArgs,
groupBy, having, sortOrder);
c.setNotificationUri(getContext().getContentResolver(), uri);
return c;
}
вот его!
здесь код, который я использую для выполнения:
Cursor showsc = getContext().getContentResolver().query(
WhatsOnTVProvider.CONTENT_EPISODES_NEXT_URI,
EpisodenTable.allColums_inclCount,
String.valueOf(Calendar.getInstance().getTimeInMillis() / 1000)
+ " < date", null, null);
Недавно я боролся с этой проблемой, обращаясь к базе данных CallLog.Calls (где мы не смогли изменить ContentProvider). В итоге мы создали запрос, который выглядел так:
SELECT _id, date, duration, type, normalized_number FROM calls WHERE _id IN (
SELECT _id FROM calls WHERE date < ? GROUP BY normalized_number ORDER BY date DESC LIMIT ?
);
Идея в том, что мы помещаем любой допустимый sqlite в наш подзапрос, возвращаем список идентификаторов и затем снова запрашиваем все вызовы с этими идентификаторами.
Окончательный код выглядел примерно так:
String whereClause = "_id IN (SELECT _id FROM calls WHERE data < ? GROUP BY normalized_number ORDER BY date DESC LIMIT ?)";
Cursor cursor = context.getContentResolver().query(
CallLog.Calls.CONTENT_URI,
new String[] { "_id", "date", "duration", "normalized_number" },
whereClause,
new String[]{ String.valueOf(amount), String.valueOf(dateFrom) },
null
);
...
В случае, если вы запрашиваете контакты, это будет выглядеть примерно так:
String whereClause = "_id IN (SELECT _id FROM contacts WHERE " + Phone.NUMBER + " LIKE ? OR " + Email.DATA + " LIKE ? GROUP BY " + Data.RAW_CONTACT_ID + " ORDER BY lower(" + Data.DISPLAY_NAME + ") ASC)";
String[] params = new String["%test%", "%test%"];
Cursor cursor = getContentResolver().query(
Data.CONTENT_URI,
new String[] { Data._ID, Data.RAW_CONTACT_ID },
whereClause,
params,
null
);
Будет некоторое снижение производительности (поскольку мы, по сути, дважды запрашиваем одинаковые результаты), но это, безусловно, будет намного быстрее, чем запросы для всех вызовов и выполнение работы GROUP BY
в мире Java, а также позволит вам создать запрос с дополнительными пунктами.
Надеюсь это поможет. Мы использовали это на Oreo, и это удовлетворило наши потребности.