It meets your requirements. Change the @op parameter to MIN, MAX, or AVG and the @Group parameter to the size of the group. The NTILE ranking NTILE used to separate groups, and then ROW_NUMBER to identify the first / lat member of each group.
DECLARE @t TABLE (id INT, VALUE REAL ) INSERT @t (id,VALUE) VALUES (1, 6.7), (2, 8.9), (3, 4.5), (5, 3.2), (8, 2.5), (9, 2.1), (10, 1.0), (15, 2.3), (18, 2.4), (19, 4.0), (20, 3.2) DECLARE @Group DECIMAL(5,1) = 3.0 DECLARE @Bucket INT DECLARE @op char(3) = 'MIN' --MAX, AVG SELECT @Bucket = CEILING(COUNT(1)/@Group) FROM @t ;WITH bucketCTE AS ( SELECT *,NTILE(@Bucket) OVER (ORDER BY id) bucket FROM @t ) ,rankCTE AS ( SELECT *, ROW_NUMBER() OVER (PARTITION BY bucket ORDER BY id ASC ) AS rn, ROW_NUMBER() OVER (PARTITION BY bucket ORDER BY id DESC ) AS rn2 FROM bucketCTE ) ,groupCTE AS ( SELECT AVG(VALUE) average, MIN(VALUE) minimum, MAX(VALUE) maximum, bucket FROM bucketCTE GROUP BY bucket ) SELECT r1.id minId, r2.id maxId , CASE WHEN @op = 'AVG' THEN g.average WHEN @op = 'MIN' THEN g.minimum WHEN @op = 'MAX' THEN g.maximum ELSE NULL END AS value FROM rankCTE AS r1 JOIN rankCTE AS r2 ON r2.bucket = r1.bucket AND r2.rn2 = 1 JOIN groupCTE AS g ON g.bucket = r1.bucket WHERE r1.rn = 1 ORDER BY r1.bucket
source share