我有一个相当有趣的情况。我有一个充满地址和消息的SQLite数据库(地址不是唯一的;消息是)。每条消息还有一个与之关联的日期。我想要做的是选择第一个消息的地址,消息,日期和与地址相关的消息。
所以,我想,"我只能按地址分组,每个地址只能获得一条消息,然后按日期对这些消息进行排序,并获取地址列的COUNT。"
我做到了,它有效......有点儿。它获取正确的计数,每个地址只获取一条消息,并按日期排序 - 但它不会为地址选择最新的消息。它似乎是任意的。
例如,我有三条消息(最早到最新)A,B,C来自地址Y,三条消息D,E,F来自地址Z.查询可以获取消息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"
,那么它们按日期排序,但当然它们都是个体而不是分组(因为日期不同)。
如何在不删除GROUP
子句的情况下为每个地址选择最新的消息(因为COUNT()
依赖于此)?我需要两个查询吗?
修改:根据the answer @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
);
答案 0 :(得分:15)
SQLite的扩展程序使greatest-n-per-group问题变得更加容易:
如果您正在使用MAX()
或MIN()
聚合函数,并且如果您同时选择其他列而不在聚合函数中使用它们或按它们进行分组,则这些列的结果值为保证出现具有最大/最小值的相同记录。 (这在其他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
答案 1 :(得分:5)
解决了!我最终结合使用了@CL.的方法和我在编辑后的帖子中概述的方法(this answer中澄清,由@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
);
它完美无缺!