actaram
actaram

Reputation: 2058

How to get the maximum and minimum values in each group as well as their times?

Let's say I have a table MyTable with the columns Id, NumericValue and UTCTimestamp. I'm trying to group the results of my table MyTable by the hour of their timestamp and return the maximum NumericValuefor each group with its associated timestamp as well as the minimum NumericValue for each group with its associated timestamp value.

For now, I'm able to achieve the first part of my problem with the following query:

SELECT 
    HOUR(t.UTCTimestamp) AS `Hour`, 
    t.NumericValue AS MaximumValue,
    t.UTCTimestamp AS MaximumValueTime
FROM MyTable t
INNER JOIN (
    SELECT HOUR(t2.UTCTimestamp) AS `Hour`, MAX(t2.NumericValue) AS NumericValue
    FROM MyTable t2
    GROUP BY HOUR(t2.UTCTimestamp)
) maxNumericValue ON HOUR(t.UTCTimestamp) = maxNumericValue.`Hour` AND t.NumericValue = maxNumericValue.NumericValue
GROUP BY HOUR(t.UTCTimestamp);

Which was inspired by this answer.

Here's an MVCE.

How could I also show the minimum value for each group as well as the timestamp associated to it?

Upvotes: 2

Views: 135

Answers (3)

Uueerdo
Uueerdo

Reputation: 15961

You can join to MyTable twice (and only use one aggregating subquery)

SELECT bounds.`Hour`
    , minT.NumericValue AS MinValue
    , minT.UTCTimestamp AS MinTime
    , maxT.NumericValue AS MaximumValue
    , maxT.UTCTimestamp AS MaximumValueTime
FROM (
    SELECT HOUR(t2.UTCTimestamp) AS `Hour`
         , MAX(t2.NumericValue) AS maxValue
         , MIN(t2.NumericValue) AS minValue
    FROM MyTable t2
    GROUP BY HOUR(t2.UTCTimestamp)
) bounds 
LEFT JOIN MyTable minT ON bounds.`Hour` = HOUR(minT.UTCTimestamp)
   AND bounds.minValue = minT.NumericValue
LEFT JOIN MyTable maxT ON bounds.`Hour` = HOUR(maxT.UTCTimestamp)
   AND bounds.maxValue = maxT.NumericValue
;

Upvotes: 1

Lukasz Szozda
Lukasz Szozda

Reputation: 175944

Starting from MySQL 8.0 you could use ROW_NUMBER:

WITH cte AS (
  SELECT *,ROW_NUMBER() OVER(PARTITION BY HOUR(UTCTimestamp) 
                              ORDER BY UTCTimestamp ASC)  AS rn
          ,ROW_NUMBER() OVER(PARTITION BY HOUR(UTCTimestamp) 
                              ORDER BY UTCTimestamp DESC) AS rn2
  FROM MyTable
)
SELECT HOUR(c1.UTCTimestamp),
     c1.ID, c1.NumericValue, c1.UTCTimestamp,  -- min row
     c2.ID, c2.NumericValue, c2.UTCTimestamp   -- max row
FROM cte c1
JOIN cte c2
  ON HOUR(c1.UTCTimestamp) = HOUR(c2.UTCTimestamp)
  AND c1.rn=1
  AND c2.rn2=1
ORDER BY HOUR(c1.UTCTimestamp) ASC;

DBFiddle Demo

Upvotes: 2

Daniel Marcus
Daniel Marcus

Reputation: 2686

Apply the same technique but with minimum:

select a.*, b.MinimumValueTime from (
SELECT 
    HOUR(t.UTCTimestamp) AS `Hour`, 
    t.NumericValue AS MaximumValue,
    t.UTCTimestamp AS MaximumValueTime
FROM MyTable t
INNER JOIN (
    SELECT HOUR(t2.UTCTimestamp) AS `Hour`, MAX(t2.NumericValue) AS NumericValue
    FROM MyTable t2
    GROUP BY HOUR(t2.UTCTimestamp)
) maxNumericValue ON HOUR(t.UTCTimestamp) = maxNumericValue.`Hour` AND t.NumericValue = maxNumericValue.NumericValue
GROUP BY HOUR(t.UTCTimestamp))a
join
(
SELECT 
    HOUR(t.UTCTimestamp) AS `Hour`, 
    t.NumericValue AS MinimumValue,
    t.UTCTimestamp AS MinimumValueTime
FROM MyTable t
INNER JOIN (
    SELECT HOUR(t2.UTCTimestamp) AS `Hour`, MIN(t2.NumericValue) AS NumericValue
    FROM MyTable t2
    GROUP BY HOUR(t2.UTCTimestamp)
) minNumericValue ON HOUR(t.UTCTimestamp) = minNumericValue.`Hour` AND t.NumericValue = minNumericValue.NumericValue
GROUP BY HOUR(t.UTCTimestamp))b on a.hour=b.hour

Upvotes: 1

Related Questions