Подтвердить что ты не робот

Группа В ContentResolver в мороженом сэндвич

Я делаю запрос в 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});
    }
}
4b9b3361

Ответ 1

Вы можете создать пользовательский Uri, который, когда ваш UriMatcher в вашем ContentProvider получит его, вы можете вставить предложение group by, а затем выполнить необработанный sql непосредственно в базе данных.

Ответ 2

сначала извините мой НЕПРАВИЛЬНЫЙ английский! Я новичок в 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);

Ответ 3

Недавно я боролся с этой проблемой, обращаясь к базе данных 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, и это удовлетворило наши потребности.