注意:因为处理顺序RANK/DENSE_RANK
是PARTITION BY
then ORDER BY
,所以这些函数在这种情况下没有用。也许,在某个时间点,MS 会因此引入一种补充语法:
[DENSE_]RANK() OVER(ORDER BY fields PARTITION BY fields)
soORDER BY
将首先被处理,然后PARTITION BY
.
1)第一个解决方案(SQL2005+)
DECLARE @TestData TABLE
(
Dt SMALLDATETIME PRIMARY KEY,
Heat TINYINT NOT NULL
);
INSERT @TestData(Dt, Heat)
VALUES
SELECT '2012-01-01T12:00:00', 8 UNION ALL SELECT '2012-01-01T12:03:00', 9 UNION ALL SELECT '2012-01-01T12:06:00', 5
UNION ALL SELECT '2012-01-01T12:09:00', 3 UNION ALL SELECT '2012-01-01T12:12:00', 6 UNION ALL SELECT '2012-01-01T12:15:00', 7
UNION ALL SELECT '2012-01-01T12:18:00', 1 UNION ALL SELECT '2012-01-01T12:21:00', 12 UNION ALL SELECT '2012-01-01T12:24:00', 28
UNION ALL SELECT '2012-01-01T12:27:00', 25 UNION ALL SELECT '2012-01-01T12:30:00', 20 UNION ALL SELECT '2012-01-01T12:33:00', 20
UNION ALL SELECT '2012-01-01T12:36:00', 20 UNION ALL SELECT '2012-01-01T12:39:00', 12 UNION ALL SELECT '2012-01-01T12:42:00', 6
UNION ALL SELECT '2012-01-01T12:45:00', 3 UNION ALL SELECT '2012-01-01T12:48:00', 5 UNION ALL SELECT '2012-01-01T12:51:00', 7
UNION ALL SELECT '2012-01-01T12:54:00', 11 UNION ALL SELECT '2012-01-01T12:57:00', 12 UNION ALL SELECT '2012-01-01 13:00:00', 6;
SET STATISTICS IO ON;
WITH CteSource
AS
(
SELECT a.*,
CASE
WHEN a.Heat >= 0 AND a.Heat <= 7 THEN 1
WHEN a.Heat >= 8 AND a.Heat <= 12 THEN 2
WHEN a.Heat > 12 THEN 3
END AS Grp,
ROW_NUMBER() OVER(ORDER BY a.Dt) AS RowNum
FROM @TestData a
), CteRecursive
AS
(
SELECT s.RowNum,
s.Dt,
s.Heat,
s.Grp,
1 AS DENSE_RANK_OVER_ORDERBY_PARTITIONBY
FROM CteSource s
WHERE s.RowNum = 1
UNION ALL
SELECT crt.RowNum,
crt.Dt,
crt.Heat,
crt.Grp,
CASE
WHEN crt.Grp = prev.Grp THEN prev.DENSE_RANK_OVER_ORDERBY_PARTITIONBY
ELSE prev.DENSE_RANK_OVER_ORDERBY_PARTITIONBY + 1
END
FROM CteSource crt
INNER JOIN CteRecursive prev ON crt.RowNum = prev.RowNum + 1
)
SELECT r.DENSE_RANK_OVER_ORDERBY_PARTITIONBY,
MAX(r.Grp) AS Grp,
COUNT(*) AS Cnt,
MIN(r.Dt) AS MinDt,
MAX(r.Dt) AS MaxDt
FROM CteRecursive r
GROUP BY r.DENSE_RANK_OVER_ORDERBY_PARTITIONBY;
结果:
DENSE_RANK_OVER_ORDERBY_PARTITIONBY Grp Cnt MinDt MaxDt
----------------------------------- ----------- ----------- ----------------------- -----------------------
1 2 2 2012-01-01 12:00:00 2012-01-01 12:03:00
2 1 5 2012-01-01 12:06:00 2012-01-01 12:18:00
3 2 1 2012-01-01 12:21:00 2012-01-01 12:21:00
4 3 5 2012-01-01 12:24:00 2012-01-01 12:36:00
5 2 1 2012-01-01 12:39:00 2012-01-01 12:39:00
6 1 4 2012-01-01 12:42:00 2012-01-01 12:51:00
7 2 2 2012-01-01 12:54:00 2012-01-01 12:57:00
8 1 1 2012-01-01 13:00:00 2012-01-01 13:00:00
2)第二种解决方案(SQL2012;更好的性能)
SELECT d.DENSE_RANK_OVER_ORDERBY_PARTITIONBY,
MAX(d.Grp) AS Grp,
MIN(d.Dt) AS MinDt,
MAX(d.Dt) AS MaxDt
FROM
(
SELECT c.*,
1+SUM(c.IsNewGroup) OVER(ORDER BY c.Dt) AS DENSE_RANK_OVER_ORDERBY_PARTITIONBY
FROM
(
SELECT b.*,
CASE
WHEN LAG(b.Grp) OVER(ORDER BY b.Dt) <> b.Grp THEN 1
ELSE 0
END
AS IsNewGroup
FROM
(
SELECT a.*,
CASE
WHEN a.Heat >= 0 AND a.Heat <= 7 THEN 1
WHEN a.Heat >= 8 AND a.Heat <= 12 THEN 2
WHEN a.Heat > 12 THEN 3
END AS Grp
FROM @TestData a
) b
) c
) d
GROUP BY d.DENSE_RANK_OVER_ORDERBY_PARTITIONBY;