这个答案最初是受到
获取每组分组SQL结果中最大值的记录的启发,但后来我进一步研究并找到了正确的解决方案。
CREATE TABLE T
(`id` int, `from` varchar(7), `to` varchar(7), `somedate` datetime)
;
INSERT INTO T
(`id`, `from`, `to`, `somedate`)
VALUES
(1, 'Number1', 'Number2', '2015-01-01 00:00:00'),
(2, 'Number2', 'Number1', '2015-01-02 00:00:00'),
(3, 'Number2', 'Number1', '2015-01-03 00:00:00'),
(4, 'Number3', 'Number1', '2015-01-04 00:00:00'),
(5, 'Number3', 'Number2', '2015-01-05 00:00:00');
已在MySQL 5.6.19上进行测试
SELECT *
FROM
(
SELECT *
FROM T
ORDER BY LEAST(`to`,`from`), GREATEST(`to`,`from`), somedate DESC
) X
GROUP BY LEAST(`to`,`from`), GREATEST(`to`,`from`)
结果集
id from to somedate
3 Number2 Number1 2015-01-03
4 Number3 Number1 2015-01-04
5 Number3 Number2 2015-01-05
但是,这依赖于MySQL的一些不可靠行为,这在未来版本中将会改变。MySQL 5.7 拒绝此查询,因为SELECT子句中的列与GROUP BY列没有函数依赖关系。如果它被配置为接受它(ONLY_FULL_GROUP_BY
已禁用),它就像以前的版本一样工作,但仍然不保证:“服务器可以从每个组中选择任何值,因此除非它们相同,否则所选的值是不确定的。”
因此,正确的答案似乎是这个:
SELECT T.*
FROM
T
INNER JOIN
(
SELECT
LEAST(`to`,`from`) AS LowVal,
GREATEST(`to`,`from`) AS HighVal,
MAX(somedate) AS MaxDate
FROM T
GROUP BY LEAST(`to`,`from`), GREATEST(`to`,`from`)
) v
ON T.somedate = v.MaxDate
AND (T.From = v.LowVal OR T.From = v.HighVal)
AND (T.To = v.LowVal OR T.To = v.HighVal)
结果集与上面相同,但在这种情况下,它保证保持不变,而以前你可能会根据表上的索引轻松获得不同的日期和行Number2,Number1
的ID。
只要原始数据中有两行具有完全相同的somedate
、to
和from
,它就会按预期工作。
让我们再添加一行:
INSERT INTO T (`id`, `from`, `to`, `somedate`)
VALUES (6, 'Number1', 'Number2', '2015-01-03 00:00:00');
上面的查询将返回两行数据,分别对应于
2015-01-03
。
id from to somedate
3 Number2 Number1 2015-01-03
6 Number1 Number2 2015-01-03
4 Number3 Number1 2015-01-04
5 Number3 Number2 2015-01-05
为了解决这个问题,我们需要一种方法来选择组中的唯一一行。在这个例子中,我们可以使用唯一的ID来打破平局。如果在组中有多行具有相同的最大日期,我们将选择具有最大ID的行。
内部子查询称为“Groups”,只是返回所有的组,就像问题中的原始查询一样。然后我们在结果集中添加一个列“id”,并选择属于同一组且具有最高“somedate”和最高“id”的“id”,这是通过“ORDER BY”和“LIMIT”完成的。这个子查询被称为“GroupsWithIDs”。一旦我们拥有了所有的组和每个组的正确行的“id”,我们就将其与原始表连接起来,以获取找到的“id”的其余列。
最终查询
SELECT T.*
FROM
(
SELECT
Groups.N1
,Groups.N2
,
(
SELECT T.id
FROM T
WHERE
LEAST(`to`,`from`) = Groups.N1 AND
GREATEST(`to`,`from`) = Groups.N2
ORDER BY T.somedate DESC, T.id DESC
LIMIT 1
) AS id
FROM
(
SELECT LEAST(`to`,`from`) AS N1, GREATEST(`to`,`from`) AS N2
FROM T
GROUP BY LEAST(`to`,`from`), GREATEST(`to`,`from`)
) AS Groups
) AS GroupsWithIDs
INNER JOIN T ON T.id = GroupsWithIDs.id
最终结果集
id from to somedate
4 Number3 Number1 2015-01-04
5 Number3 Number2 2015-01-05
6 Number1 Number2 2015-01-03