简体   繁体   中英

Returning the next-to-last entry using MySQL

A little info: people check-in but they don't check out. Each check-in creates an auto-incremented entry into the _checkins table with a timestamp, MemberID, etc.

Here's the data the query needs to return:

  1. Member info (name, picture, ID, etc)
  2. The number of check-ins they've had in the last 30 days
  3. The time since they're last check-in must be less than 2 hours for them to be on the list.
  4. The date of their last check-in NOT COUNTING TODAY (in other words, the next to last "Created" entry in the _checkins table).

I have it all working except the last part. I feel like LIMIT is going to be part of the solution but I just can't find a way to implement it correctly. Here's what I've got so far:

SELECT m.ImageURI, m.ID, m.FirstName, m.LastName, 
ROUND(time_to_sec(timediff(NOW(), MAX(ci.Created))) / 3600, 1) as 
'HoursSinceCheckIn', CheckIns
FROM _checkins ci LEFT JOIN _members m ON ci.MemberID = m.ID
INNER JOIN(SELECT MemberID, COUNT(DISTINCT ID) as 'CheckIns'
                       FROM _checkins
          WHERE(
                       Created BETWEEN NOW() - INTERVAL 30 DAY AND NOW()
                       )
          GROUP BY MemberID
          ) lci ON ci.MemberID=lci.MemberID

          WHERE( 
                       ci.Created BETWEEN NOW() - INTERVAL 30 DAY AND NOW()
                       AND TIMESTAMPDIFF(HOUR, ci.Created, NOW()) < 2
                       AND ci.Reverted = 0
          )
          GROUP BY m.ID
    ORDER BY CheckIns ASC

You can simplify greatly (and make your code safer, as well):

SELECT _Members.ImageURI, _Members.ID, _Members.FirstName, _Members.LastName, 
       ROUND(TIME_TO_SEC(TIMEDIFF(NOW(), _FilteredCheckins.lastCheckin)) / 3600, 1) AS hoursSinceCheckIn, _FilteredCheckins.checkIns,
       (SELECT MAX(_Checkins.created)
        FROM _Checkins
        WHERE _Checkins.memberId = _Members.ID
              AND _Checkins.created < _FilteredCheckins.lastCheckin) AS previousCheckin
FROM _Members
JOIN (SELECT memberId, COUNT(*) AS checkIns, MAX(created) AS lastCheckin
      FROM _Checkins
      WHERE created >= NOW() - INTERVAL 30 DAY
      GROUP BY memberId
      HAVING lastCheckin >= NOW() - INTERVAL 2 HOURS) _FilteredCheckins
  ON _FilteredCheckins.memberId = _Members.ID
ORDER BY _FilteredCheckins.checkIns ASC
  • We're counting all checkins in the last 30 days, including the most recent, but that's trivially adjustable.
  • I'm assuming _Checkins.id is unique (it should be), so COUNT(DISTINCT ID) can be simplified to COUNT(*) . If this isn't the case you'll need to put it back.

(Side note: please don't use BETWEEN , especially with date/time types )

(humorous side note: I keep mentally reading this as "chickens"....)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM