在组之前选择订单

时间:2014-08-24 06:51:45

标签: mysql sql greatest-n-per-group

这让我感到沮丧,我已经搜索了一段时间。

id                   int(11)                
target_id            int(11)    
text                 text   latin1_swedish_ci
ip_address           varchar(20)    latin1_swedish_ci
created_by_user_id   int(11)    
created_date         datetime   

数据示例:

id target_id    ip_address    created_by_user_id    text    created_date
1  113          192.168.0.1   1                     test1   2014-08-24 12:32:01
1  113          192.168.0.1   1                     test2   2014-08-24 13:32:01 
1  1            192.168.0.1   113                   test3   2014-08-21 13:32:01  
1  1            192.168.0.1   113                   test4   2014-08-22 13:32:01 

如何选择最新结果,并按target_id分组?我已经尝试了以下SQL语句来正确分组,但它没有带来最新的结果。

SELECT
    *
FROM
    `fq_message`
GROUP BY
    target_id
ORDER BY
    id DESC

返回:

1  113          192.168.0.1   1                     test1   2014-08-24 12:32:01
1  1            192.168.0.1   113                   test3   2014-08-21 13:32:01 

我想实现

1  113          192.168.0.1   1                     test2   2014-08-24 13:32:01 
1  1            192.168.0.1   113                   test4   2014-08-22 13:32:01

2 个答案:

答案 0 :(得分:3)

MySQL允许您编写正式不是SQL的查询。例如,以下查询可能在MySQL中有效,或者可能不在。一个理智的数据库肯定会拒绝它:

select  *
from    (
        select  *
        from    fq_message
        order by
                created_date desc
        ) SubQueryAlias
 group by
        target_id

这告诉MySQL在分组之前进行排序,如你所知。但这不是真正的SQL。 SQL标准说子查询集和表是无序的。所有其他数据库都会拒绝内部查询,因为它会强制执行无关紧要的排序顺序。

该查询还会选择*,但target_id上的群组。 MySQL将获取它遇到的第一个值,如果它遵循子查询排序,则是每个目标的最新值。但不能保证它会这样做。 MySQL如何处理这些事情甚至可以根据MySQL版本进行更改。

更好的方法是编写需要MySQL做正确事情的SQL。其中一种方法是使用每个目标的最新日期集合进行过滤连接:

select  msg.*
from    fq_message msg
join    (
        select  target_id
        ,       max(created_date) as max_date_for_this_target
        from    fq_message
        group  by
                target_id
        ) filter
on      msg.target_id = filter.target_id
        and msg.created_date = filter.max_date_for_this_target

joininner join,只会返回on子句找到匹配项的行。结果是过滤掉了没有目标最新日期的消息。

有关更多方法和示例,请参阅标记。

答案 1 :(得分:0)

试试他的代码:

SELECT
    ID, 
    target_id, 
    ID_ADDRESS, 
    CREATED_BY_USER_ID, 
    (SELECT TEXT FROM [YOUR_TABLE_NAME] WHERE ID = TMP.ID AND TARGET_ID = TMP.TARGET_ID AND ID_ADDRESS = TMP.ID_ADDRESS AND CREATED_BY_USER_ID = TMP.CREATED_BY_USER_ID AND CREATED_DATE = TMP.CREATED_DATE), 
    CREATED_DATE

FROM
(
    SELECT 

        ID, 
        target_id, 
        ID_ADDRESS, 
        CREATED_BY_USER_ID,
        MAX(CREATED_DATE) AS CREATED_DATE 

    FROM 
    /* Instead of this from, use from [your table] */
    (
        SELECT 1 ID, 113 TARGET_ID, '192.168.0.1' ID_ADDRESS, 1   CREATED_BY_USER_ID, 'test1' TEXT, TO_DATE('2014-08-24 12:32:01', 'YYYY-MM-DD HH24:MI:SS') CREATED_DATE FROM DUAL UNION
        SELECT 1   , 113          , '192.168.0.1'           , 1                     , 'test2'     , TO_DATE('2014-08-24 13:32:01', 'YYYY-MM-DD HH24:MI:SS')              FROM DUAL UNION
        SELECT 1   , 1            , '192.168.0.1'           , 113                   , 'test3'     , TO_DATE('2014-08-21 13:32:01', 'YYYY-MM-DD HH24:MI:SS')              FROM DUAL UNION
        SELECT 1   , 1            , '192.168.0.1'           , 113                   , 'test4'     , TO_DATE('2014-08-22 13:32:01', 'YYYY-MM-DD HH24:MI:SS')              FROM DUAL
    )
    GROUP BY ID, target_id, ID_ADDRESS, CREATED_BY_USER_ID
) TMP
ORDER BY ID, CREATED_DATE DESC

<强>结果:

1   113 192.168.0.1 1   8/24/2014 1:32:01 PM
1   1   192.168.0.1 113 8/22/2014 1:32:01 PM

在Oracle中测试