Group By в ContentResolver в сэндвич с мороженым - PullRequest
18 голосов
/ 28 декабря 2011

Я делаю запрос на Android Content Content Provider.Мне нужно предложение 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 обнаруживает это и добавляет правильное число скобок, чтобы предотвратить мою инъекцию.Любой другой способ сделать это в одном запросе курсора?

Edit

В настоящее время я удалил 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});
    }
}

Ответы [ 3 ]

2 голосов
/ 11 июля 2019

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

1 голос
/ 30 мая 2014

Прежде всего, извините, мой плохой английский!Я новичок в 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)

«функция» запроса провайдера контента дает вам только:

query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder)

здесь uя могу опубликовать мой код:

    @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);
1 голос
/ 18 января 2014

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

...