汇总第一行和最后一行的值

时间:2012-01-22 21:57:51

标签: mysql select join aggregate

我有一个USERS表。

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

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

我想为第一个和最后一个连接选择userID和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上使用的值和在最后一个上使用的值。

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

3 个答案:

答案 0 :(得分: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

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

答案 1 :(得分:1)

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

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

执行此操作的规范方法是通过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;

这基本上会在CONNECTIONS上将userid加入到自身,并在该用户ID中c.dateconn<c2.dateconn内形成每对可能的连接日期。如果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;

选择最新版本是相同的,但您将>撤消到<

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;

您的查询有点复杂,因为您不仅要选择最小值或最大值,而且两者 min 最大值

解决方案

我认为你可以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>=c.dateconn量词(它只需要加入userid),但它会使我们拥有的行缩小加入。这是因为我们在(c3,c4)表中寻找最新的(即MAX)日期,并且c包含MIN日期,我们只需要查看MAX日期所在的行&gt; ; = MIN日期。

答案 2 :(得分:0)

尝试LEFT JOIN而不是JOIN。 在ORDER BY之前添加GROUP BY USERS.id