SQL如何按两列分组

时间:2015-03-03 16:49:13

标签: mysql sql

贝娄是一个示例表。

ID   FROM       TO         DATE  
1    Number1    Number2    somedate
2    Number2    Number1    somedate
3    Number2    Number1    somedate
4    Number3    Number1    somedate
5    Number3    Number2    somedate

预期结果是每对唯一的TO和FROM列

获得1行

按ID ASC排序的示例结果

(1,Number1,Number2)
(4,Number3,Number1)
(5,Number3,Number2)

好的我已经找到了如何使用以下查询执行此操作

SELECT * FROM table GROUP BY LEAST(to,from), GREATEST(to,from)

但是我无法获得每个唯一对的最新记录。

我尝试使用order by ID desc,但它会返回唯一对的第一个找到的行。

3 个答案:

答案 0 :(得分:4)

SQL小提琴因为某些原因而无法工作,所以在这段时间内你需要帮助我。

假设以下声明有效

SELECT 
    LEAST(to,from) as LowVal, 
    GREATEST(to,from) as HighVal, 
    MAX(date) as MaxDate
FROM table 
GROUP BY LEAST(to,from), GREATEST(to,from)

那么你可以加入

select t.*
from 
    table t
    inner join 
        (SELECT 
            LEAST(to,from) as LowVal, 
            GREATEST(to,from) as HighVal, 
            MAX(date) as MaxDate
        FROM table 
        GROUP BY LEAST(to,from), GREATEST(to,from)
        ) v
        on t.date = v.MaxDate
        and (t.From = v.LowVal or t.From = v.HighVal)
        and (t.To = v.LowVal or t.To= v.HighVal)

答案 1 :(得分:0)

我相信以下内容可行,我的知识是Microsoft SQL Server,而不是MySQL。如果MySQL缺少其中一个,请告诉我,我将删除答案。

DECLARE @Table1 TABLE(
ID int,
Too varchar(10),
Fromm varchar(10),
Compared int)

INSERT INTO @Table1 values (1, 'John','Mary', 2), (2,'John', 'Mary', 1), (3,'Sue','Charles',1), (4,'Mary','John',3)


SELECT ID, Too, Fromm, Compared
FROM @Table1 as t
INNER JOIN
(
SELECT
    CASE WHEN Too < Fromm THEN Too+Fromm
    ELSE Fromm+Too
    END as orderedValues, MIN(compared) as minComp
FROM @Table1
GROUP BY    CASE WHEN Too < Fromm THEN Too+Fromm
ELSE Fromm+Too
END
) ordered ON 
ordered.minComp = t.Compared 
AND ordered.orderedValues = 
        CASE 
            WHEN Too < Fromm 
                THEN Too+Fromm
            ELSE 
                Fromm+Too
        END

我使用的是int而不是time值,但它的工作方式相同。它很脏,但它给了我预期的结果。

它的基础是使用派生查询,您可以在其中获取要获取唯一值的两列,并使用case语句将它们组合成标准格式。在这种情况下,较早的字母顺序与后面的值按字母顺序连接。使用该值来获取我们要查找的最小值,返回到原始表以再次分离出值以及该表中的其他内容。假设我们聚合的值将是唯一的,所以在这种情况下,如果有(1,'John','Mary',2)和(2,'Mary','John',2),它会破坏并返回那对夫妇的2条记录。

答案 2 :(得分:0)

这个答案最初的灵感来自Get records with max value for each group of grouped SQL results 但后来我进一步观察并提出了正确的解决方案。

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 rejects此查询,因为SELECT子句中的列在功能上不依赖于GROUP BY列。如果它被配置为接受它(ONLY_FULL_GROUP_BY被禁用),它就像以前的版本一样,但它仍然不是guaranteed:“服务器可以自由选择每个组中的任何值,所以除非它们是相同的,所选择的值是不确定的。“

所以,正确答案似乎是这样的:

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之前,具体取决于您在表上的索引

在原始数据中有两行具有完全相同的somedateto以及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,我们选择属于同一组的id,其中somedate最高,id最高,ORDER BY LIMITGroupsWithIDs。此子查询称为id。我们拥有所有群组并为每个群组设置了join正确的行后,我们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 s获取剩余的列。

最终查询

id from    to      somedate
4  Number3 Number1 2015-01-04
5  Number3 Number2 2015-01-05
6  Number1 Number2 2015-01-03

最终结果集

{{1}}