18

我有一个相当有趣的情况。我有一个充满地址和消息的 SQLite 数据库(地址不是唯一的;消息是)。每条消息还有一个与之关联的日期。我要做的是选择第一条消息的地址、消息、日期以及与该地址关联的消息数量。

所以,我想,“我可以按地址分组,每个地址只收到一条消息,然后按日期排序,并获取地址列的 COUNT。”

我做到了,它的工作原理......有点。它获取正确的计数,每个地址仅获取一条消息,并按日期对它们进行排序——但它不选择地址的最新消息。这似乎是任意的。

例如,我有来自地址 Y 的三条消息(从最早到最新)A、B、C,以及来自地址 Z 的三条消息 D、E、F。查询可能会获取消息 B 和 E,然后按日期对它们进行排序。它应该获取消息 C 和 F,并按日期对它们进行排序。

这是我到目前为止所拥有的:

// Expanded version:
Cursor cursor = db.query(
    /* FROM */ "messages_database",
    /* SELECT */ new String[]{ "*", "COUNT(address) AS count" },
    /* WHERE */ null,
    /* WHERE args */ null,
    /* GROUP BY */ "address",
    /* HAVING */ null,
    /* ORDER BY */ "date DESC"
);

// Or, same code on one line:
Cursor cursor = db.query("messages_database", new String[]{ "*", "COUNT(address) AS count" }, null, null, "address", null, "date DESC");

我觉得这可能与HAVING条款有关,但我真的不知道。我在 PHP 中经常使用 MySQL,但以前从未接触过HAVING。我尝试将我的HAVING子句设置为"MAX(date)",但没有效果。如果我将我的GROUP BY子句设置为"address, date",那么它们按日期排序,但当然它们都是单独的而不是分组的(因为日期不同)。

谷歌搜索被证明是徒劳的;像“ android sqlite order before group ”和“ android sqlite group by order ”这样的查询不会产生相关结果。

如何在不删除我的子句的情况下为每个地址选择一条最新消息(依赖于此)?我需要两个查询吗?GROUPCOUNT()

编辑:根据@Adrian 在评论中链接我的答案,我想出了两个查询,它们都产生了相同的结果;一行,其中计数为 7(这是地址的总数,而不是每个地址的消息),并且显示的地址不是最新消息的地址。

这两个查询是:

Cursor cursor = db.rawQuery(
      "SELECT t.*, COUNT(t.message_content) AS count "
    + "FROM messages_database t "
    + "INNER JOIN ("
    + "    SELECT address, MAX(date) AS maxdate "
    + "    FROM messages_database "
    + "    GROUP BY address "
    + ") ss ON t.address = ss.address AND t.date = ss.maxdate",
    null
);
Cursor cursor = db.rawQuery(
      "SELECT t1.*, COUNT(t1.message_content) AS count "
    + "FROM messages_database t1 "
    + "LEFT OUTER JOIN messages_database t2 "
    + "ON (t1.address = t2.address AND t1.date < t2.date) "
    + "WHERE t2.address IS NULL",
    null
);
4

2 回答 2

16

SQLite 有一个扩展,可以让问题变得更容易:
如果您使用MAX()orMIN()聚合函数,并且如果您同时选择其他列而不在聚合函数中使用它们或按它们分组,那么这些列的结果值保证来自具有最大值/最小值的同一记录。(这在其他 SQL 方言中是不允许的,并且在SQLite 3.7.11中引入。)

因此,对于您的问题,您可以使用如下查询:

SELECT *, COUNT(address) AS count, MAX(date)
FROM messages_database
GROUP BY address

如果您没有 SQLite 3.7.11(可能在大多数 Android 版本上)或使用其他 SQL 引擎,则以下查询将起作用:

SELECT *,
       (SELECT COUNT(address) AS count
        FROM messages_database m2
        WHERE m1.address = m2.address)
FROM messages_database m1
WHERE date = (SELECT MAX(date)
              FROM messages_database m3
              WHERE m1.address = m3.address)
GROUP BY address
于 2012-09-26T18:56:33.553 回答
7

解决了!我最终使用了@CL 的组合。的方法和我在编辑的帖子中概述的方法(在这个答案中澄清,由@Adrian发布)。

因为我不想使用 3 个SELECT语句(如@CL. 的回答所述),所以我使用了与INNER JOIN其他语句相同的概念,同时保留了他的方法。

结果是这样的:

Cursor cursor = db.rawQuery(
      "SELECT t.*, ss.count AS count "
    + "FROM messages_database t "
    + "INNER JOIN ("
    + "    SELECT address, MAX(date) AS maxdate, COUNT(address) AS count "
    + "    FROM messages_database "
    + "    GROUP BY address "
    + ") ss ON t.address = ss.address AND t.date = ss.maxdate "
    + "GROUP BY t.address "
    + "ORDER BY t.date DESC ",
    null
);

它运行良好!

于 2012-09-26T19:27:10.670 回答