将分组列添加到有序表

时间:2013-08-11 14:19:24

标签: sql sql-server sql-server-2008

我有下表:

|id|sog|zoneId|
| 1| 20|     2|
| 2|  2|     2|
| 3| 10|     2|
| 4| 10|     1|
| 5|  3|     1|
| 6| 8 |     2|
| 7| 1 |     2|
| 8| 3 |     2|
| 9| 4 |     2|

行按id列排序。

我想提出一个要返回的查询:

|id|sog|zoneId|grpId|
| 1| 20|     2|    1|
| 2|  2|     2|    1|
| 3| 10|     2|    1|
| 4| 10|     1|    2|
| 5|  3|     1|    2|
| 6| 8 |     2|    3|
| 7| 1 |     2|    3|
| 8| 3 |     2|    3|
| 9| 4 |     2|    3|

2 个答案:

答案 0 :(得分:2)

以下是基于Itzik Ben-Gan's solution to islands and gaps的解决方案:

;with q as (
    select *,
           row_number() over (order by id)
         - row_number() over (order by zoneid, id) as grp
      from thetable
)
select id, sog, zoneid,
       min (id) over(partition by zoneid, grp) as grp
  from q
 order by id

Grp与两个序列保持一致,idZoneID, id一起运行,但这些数字既不是有序的也不是连续的。通过每组取min(id)简单地构造有序组号。如果您需要顺序组号,请添加另一个检索dense_rank() over(order by grp)的cte。

Sql Fiddle this way。 (感谢w0lf)。

答案 1 :(得分:1)

如果你的Id是顺序的并且没有间隙,那么我认为最快的方式是:

;with cte as (
    select
        T.id, T.sog, T.zoneId,
        1 as grpId
    from Table1 as T
    where T.id = 1

    union all

    select
        T.id, T.sog, T.zoneId,
        c.grpId + case when T.zoneId = c.zoneId then 0 else 1 end as grpId
    from cte as c
        inner join Table1 as T on T.id = c.id + 1
)
select c.id, c.sog, c.zoneId, c.grpId
from cte as c

sql fiddle demo

如果Id不是连续的或有间隙,您可以这样做:

;with cte1 as (
    select
        T.id, T.sog, T.zoneId,
        row_number() over (order by T.id) as row_num
    from Table1 as T
), cte2 as (
    select
        T.id, T.sog, T.zoneId, T.row_num,
        1 as grpId
    from cte1 as T
    where T.row_num = 1

    union all

    select
        T.id, T.sog, T.zoneId, T.row_num,
        c.grpId + case when T.zoneId = c.zoneId then 0 else 1 end as grpId
    from cte2 as c
        inner join cte1 as T on T.row_num = c.row_num + 1
)
select c.id, c.sog, c.zoneId, c.grpId
from cte2 as c

sql fiddle demo

但我不确定性能是否适合