【问题标题】:Aggregate values from first and last row汇总第一行和最后一行的值
【发布时间】:2012-02-16 09:59:06
【问题描述】:

我有一个 USERS 表。

每个用户在 CONNECTIONS 表中都有连接。

每个连接都有一个日期时间和一些引用属性,如时区,存储在 TZ 引用表中。

我想为第一个和最后一个连接选择用户 ID 和 TimeZoneLabel。即使用户没有连接(因此会显示 NULL 或其他任何内容)

做一些类似的事情:

Select USERS.id,
min(TZ.label),
max(TZ.label)

from USERS
join CONNECTION on USERS.id = CONNECTIONS.userid
join TZ on TZ.id = CONNECTIONS.tzid

group by USERS.id
order by max(CONNECTIONS.dateconn)

但我无法做到这一点。我在网上找到了有关此的文章,但是当我尝试时没有任何效果。上面的示例不适用于标签,因为没有真正的最小/最大值,而是第一个 CONNECTION 使用的值和最后一个 CONNECTION 使用的值。

我的实际要求中有很多这样的,所以我想避免过多的子选择。

【问题讨论】:

    标签: mysql select join aggregate


    【解决方案1】:

    没有时区:

    SELECT 
           u.id             AS userId
         , MIN(c.dateconn)  AS firstConnectionDatetime
         , MAX(c.dateconn)  AS lastConnectionDateTime
    FROM Users AS u
      LEFT JOIN Connection AS c
        ON u.id = c.userid    
    GROUP BY u.id    
    ORDER BY lastConnectionDateTime
    

    有时区(假设Connection 表有idPrimary Key):

    SELECT 
           u.id             AS userId
         , ConMin.dateconn  AS firstConnectionDatetime
         , ConMax.dateconn  AS lastConnectionDateTime
         , TzMin.label      AS firstTimeZoneLabel
         , TzMax.label      AS lastTimeZoneLabel
    FROM Users AS u
      LEFT JOIN Connection AS ConMax
        ON ConMax.id =
            ( SELECT c.id
              FROM Connection AS c
              WHERE u.id = c.userid 
              ORDER BY c.dateconn DESC
              LIMIT 1
            )
      LEFT JOIN TzMax
        ON TzMax.id = ConMax.tzid
      LEFT JOIN Connection AS ConMin
        ON ConMin.id =
            ( SELECT c.id
              FROM Connection AS c
              WHERE u.id = c.userid 
              ORDER BY c.dateconn ASC
              LIMIT 1
            )
      LEFT JOIN TzMin
        ON TzMin.id = ConMin.tzid
    

    Connection 表上的复合 (userid, dateconn, id) 索引将有助于提高性能。

    【讨论】:

    • 简单... :-) 但问题是时区。我编辑我的问题以反映这一点。
    • 谢谢你,很好用,你是高手。有几天我一直在寻找这样的解决方案!
    【解决方案2】:

    这个答案有一些解释 - 你所追求的实际查询在底部。

    这是一个实例,不仅选择每组的最大/最小字段,还选择与其对应的其他字段。

    执行此操作的规范方法是通过LEFT JOIN-将表设置为自身。 例如,要选择与来自CONNECTIONS 的最新连接对应的整行,您可以:

    SELECT c.userid, c.tzid as latestTZ, c.dateconn as latestConn
    FROM CONNECTIONS c
    LEFT JOIN CONNECTIONS c2 ON c.userid=c2.userid AND c.dateconn<c2.dateconn
    WHERE c2.dateconn IS NULL
    ORDER BY c.userid;
    

    这实质上将CONNECTIONSuserid 上的自身连接起来,并在c.dateconn&lt;c2.dateconn 所在的用户ID 内形成每对可能的连接日期。如果c2 中没有日期大于c 的行,那么您选择了最大(即最近的)日期。 JOIN 确保您还可以从表中选择相应行的其余部分。

    考虑到这一点,这就是我们为每个用户选择第一个连接日期和标签的方式(如果他们从未连接过,则使用 NULL。如果您不希望这种行为(即只显示已连接)然后您可以完全忽略USERS 表)。

    SELECT u.id,c.dateconn as firstConnection,TZ.label AS firstTZ
    FROM USERS u
    LEFT JOIN CONNECTIONS c ON u.id=c.userid
    LEFT JOIN CONNECTIONS c2 ON c.userid=c2.userid AND c.dateconn > c2.dateconn
    LEFT JOIN TZ ON c.tzid=TZ.id
    WHERE c2.dateconn IS NULL;
    

    选择最新的也是一样的,只是将&gt;反转为&lt;

    SELECT u.id,c.dateconn as latestConnection,TZ.label AS latestTZ
    FROM USERS u
    LEFT JOIN CONNECTIONS c ON u.id=c.userid
    LEFT JOIN CONNECTIONS c2 ON c.userid=c2.userid AND c.dateconn < c2.dateconn
    LEFT JOIN TZ ON c.tzid=TZ.id
    WHERE c2.dateconn IS NULL;
    

    您的查询有点复杂,因为您不仅要选择最小值或最大值,还要选择 both 最小值 最大值。

    解决方案

    我认为您可能能够 UNION 前两个查询,或者您可以通过基本上 JOIN 将两个查询一起执行一次犯规完成所有操作:

    # MIN & MAX
    SELECT u.id, c.dateconn as firstCon, TZ.label as firstTZ, 
                 c3.dateconn as latestCon, TZ2.label as latestTZ
    FROM USERS u
    LEFT JOIN CONNECTIONS c ON u.id=c.userid
    LEFT JOIN CONNECTIONS c2 ON c.userid=c2.userid AND c.dateconn > c2.dateconn
    LEFT JOIN CONNECTIONS c3 ON c.userid=c3.userid AND c3.dateconn >= c.dateconn
    LEFT JOIN CONNECTIONS c4 ON c3.userid=c4.userid AND c3.dateconn < c4.dateconn
    LEFT JOIN TZ ON TZ.id=c.tzid
    LEFT JOIN TZ TZ2 ON TZ2.id=c3.tzid
    WHERE c2.dateconn IS NULL
    AND c4.dateconn IS NULL
    ORDER BY u.id;
    

    (c,c2) 对找到第一个连接日期/时区,(c3,c4) 对找到最新的。

    此外,与c3 的连接实际上并不需要c3.dateconn&gt;=c.dateconn 量词(它所需要的只是连接userid),但它额外缩小了我们必须连接的行。这是因为由于我们在 (c3,c4) 表中寻找最新(即 MAX)日期,而 c 包含 MIN 日期,我们只需要查看 MAX 日期 >= MIN 日期的行.

    【讨论】:

      【解决方案3】:

      尝试 LEFT JOIN,而不是 JOIN。 同样在 ORDER BY 之前添加一个 GROUP BY USERS.id

      【讨论】:

        猜你喜欢
        • 2012-08-26
        • 2012-10-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-08-20
        相关资源
        最近更新 更多