Android:ContentResolver 中的 Distinct 和 GroupBy

问题描述 投票:0回答:11

DISTINCT
和/或
GROUPBY
添加到基于
ContentResolver
的查询的正确方法是什么?

现在我必须为每个特殊情况创建自定义 URI。

有更好的方法吗?

(我仍然以 1.5 作为最低公分母)

android distinct group-by android-contentresolver
11个回答
43
投票

在查询 contentResolver 时,您可以做一些不错的 hack,使用:

String selection = Models.SOMETHING + "=" + something + ") GROUP BY (" + Models.TYPE;

21
投票

如果您想将 DISTINCT 与 SELECT 多于一列一起使用,则需要使用 GROUP BY。
通过 ContentResolver.query 进行的迷你黑客使用:

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());
        }

此代码从设备收件箱中为每个发件人选择最后一条短信。
注意:在 GROUP BY 之前我们总是需要至少写一个条件。 ContentResolver.query 方法中的结果 SQL 查询字符串将:

SELECT DISTINCT address, body FROM sms WHERE (type=1) AND (address IS NOT NULL) GROUP BY (address) 

15
投票

既然没有人来回答,我只想告诉你我是如何解决这个问题的。基本上,我会为每种情况创建自定义 URI,并在

selection
参数中传递条件。然后在
ContentProvider#query
中,我将识别案例并根据表名称和选择参数构造原始查询。

这是一个简单的例子:

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);

14
投票

在您重写的

ContentProvider
查询方法中,有一个特定的 URI 映射到使用不同的。

然后使用

SQLiteQueryBuilder
并调用
setDistinct(boolean)
方法。

@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;
}

7
投票

虽然我没有使用过Group By,但我在内容解析器查询中使用了Distinct

Cursor cursor = contentResolver
                .query(YOUR_URI,
                        new String[] {"Distinct "+ YOUR_COLUMN_NAME},
                        null,
                        null, 
                        null);

1
投票

在投影中添加 Distinct 关键字也对我有用,但是,只有当 unique 关键字是第一个参数时才有效:

String[] projection = new String[]{"DISTINCT " + DBConstants.COLUMN_UUID, ... };

0
投票

在某些情况下,我们可以使用“distinct(COLUMN_NAME)”作为选择, 它工作完美。 但在某些情况下,会导致异常。

当它导致异常时,我将使用 HashSet 来存储列值....


0
投票
// getting sender list from messages into spinner View
    Spinner phoneListView = (Spinner) findViewById(R.id.phone_list);
    Uri uri = Uri.parse("content://sms/inbox");     
    Cursor c = getContentResolver().query(uri, new String[]{"Distinct address"}, null, null, null);
    List <String> list;
    list= new ArrayList<String>();
    list.clear();
    int msgCount=c.getCount();
    if(c.moveToFirst()) {
        for(int ii=0; ii < msgCount; ii++) {
            list.add(c.getString(c.getColumnIndexOrThrow("address")).toString());
            c.moveToNext();
        }
    }
    phoneListView.setAdapter(new ArrayAdapter<String>(BankActivity.this, android.R.layout.simple_dropdown_item_1line, list));

0
投票

当您的投影中有多个列时,您应该这样做:

    val uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
    val projection = arrayOf(
        "DISTINCT " + MediaStore.Images.Media.BUCKET_ID,
        MediaStore.Images.Media.BUCKET_DISPLAY_NAME,
        MediaStore.Images.Media.BUCKET_ID,
        MediaStore.MediaColumns.DATA
    )
    val groupBySelection = " 1) GROUP BY (${MediaStore.Images.Media.BUCKET_ID}"
    contentResolver.query(
        uri,
        projection,
        null,
        groupBySelection,
        null,
        null
    )

带有右括号和内部数字“1”的groupBySelection是一个小技巧,但它工作得绝对没问题


0
投票

我创建了一个使用 group by 和distinct 的实用方法。

使用方法

以下是从 MMS 数据库中选择未见

thread_id
以及最后一条消息日期的示例。

query(contentResolver= contentResolver,
        select = arrayOf(Mms.THREAD_ID, "max(${Mms.DATE}) as date"),
        from = Mms.CONTENT_URI,
        where = "${Mms.SEEN} = 0",
        groupBy = "1",
        orderBy = "2 desc"
        ).use {
    while (it?.moveToNext() == true){
        val threadId = it.getInt(0)
        val date = it.getLong(1)
    }
}

来源

fun query(
        contentResolver: ContentResolver,
        from: Uri,
        select: Array<String>,
        where: String? = null,
        groupBy: Array<out String>? = null,
        distinct: Boolean = false,
        selectionArgs: Array<out String>? = null,
        orderBy: String? = null,
): Cursor? {
    val tmpSelect = select[0]
    val localWhere =
            if (groupBy == null) where
            else "${where ?: "1"}) group by (${groupBy.joinToString()}"
    if (distinct) {
        select[0] = "distinct $tmpSelect"
    }
    val query = contentResolver.query(from, select, localWhere, selectionArgs, orderBy)
    select[0] = tmpSelect
    return query
}

-1
投票

也许获得不同的值更简单, 尝试在您想要投影表的列名之前添加 DISTINCT 单词

String[] projection = new String[]{
                BaseColumns._ID,
                "DISTINCT "+ Mediastore.anything.you.want
};

并将其用作内容解析器的查询方法的参数!

我希望能帮助你,因为前几天我也有同样的问题

© www.soinside.com 2019 - 2024. All rights reserved.