用于创建团队的SQL查询

时间:2013-02-27 17:19:27

标签: sql-server

我需要一个查询来将团队分配给一系列用户。数据如下所示:

UserId  Category    Team
1       A           null
2       A           null
3       B           null
4       B           null
5       A           null
6       B           null
8       A           null
9       B           null
11      B           null

应该通过按用户ID排序来创建团队,并且第一个用户ID变为团队编号,连续的A是该团队的一部分,后面的B也是如此。在Bs开始新球队之后的第一个A.总会有至少一个A和一个B.所以在更新之后,这些数据应如下所示:

UserId  Category    Team
1       A           1
2       A           1
3       B           1
4       B           1
5       A           5
6       B           5
8       A           8
9       B           8
11      B           8

编辑: 需要补充的是,用户ID不会总是递增1.我编辑了示例数据以显示我的意思。此外,团队ID不一定必须是第一个用户的ID,只要它们最终被正确分组。例如,用户1 - 4可以全部在团队'1'上,用户5和6在团队'2'上,用户8,9和11在团队'3'上

3 个答案:

答案 0 :(得分:4)

首先,您可以使用不断增加的数字标记每一行。然后,您可以使用left join查找以前的用户。如果之前的用户具有类别'B',并且当前的一个类别'A',则表示新团队的开始。然后,团队编号是在当前UserId之前启动新团队的最后一个UserId

使用SQL Server 2008语法:

; with  numbered as
        (
        select  row_number() over (order by UserId) rn
        ,       *
        from    Table1
        )
,       changes as
        (
        select  cur.UserId
        ,       case
                when prev.Category = 'B' and cur.Category = 'A' then cur.UserId
                when prev.Category is null then cur.UserId
                end as Team
        from    numbered cur
        left join
                numbered prev
        on      cur.rn = prev.rn + 1
        )
update  t1
set     Team = team.Team
from    Table1 t1
outer apply
        (
        select  top 1 c.Team
        from    changes c
        where   c.UserId <= t1.UserId
                and c.Team is not null
        order by
                c.UserId desc
        ) as team;

Example at SQL Fiddle.

答案 1 :(得分:2)

您可以使用递归CTE执行此操作:

with userCTE as
(
  select UserId
    , Category
    , Team = UserId
  from users where UserId = 1
  union all
  select users.UserId
    , users.Category
    , Team = case when users.Category = 'A' and userCTE.Category = 'B' then users.UserId else userCTE.Team end
  from userCTE
    inner join users on users.UserId = userCTE.UserId + 1
)
update users
set Team = userCTE.Team
from users
  inner join userCTE on users.UserId = userCTE.UserId
option (maxrecursion 0)

SQL Fiddle demo

修改

您可以更新CTE以实现此目的:

with userOrder as
(
  select *
    , userRank = row_number() over (order by userId)
  from users
)
, userCTE as
(
  select UserId
    , Category
    , Team = UserId
    , userRank
  from userOrder where UserId = (select min(UserId) from users)
  union all
  select users.UserId
    , users.Category
    , Team = case when users.Category = 'A' and userCTE.Category = 'B' then users.UserId else userCTE.Team end
    , users.userRank
  from userCTE
    inner join userOrder users on users.userRank = userCTE.userRank + 1
)
update users
set Team = userCTE.Team
from users
  inner join userCTE on users.UserId = userCTE.UserId
option (maxrecursion 0)

SQL Fiddle demo

修改

对于较大的数据集,您需要添加maxrecursion查询提示;我编辑了之前的查询以显示此信息。来自联机丛书:

  

指定此查询允许的最大递归数。   number是0到32767之间的非负整数。当0为时   指定,不应用限制。

在这种情况下,我将其设置为0,即不限制递归。

Query Hints

答案 2 :(得分:0)

我实际上最终得到了以下内容。它在半小时内完成了所有300万行。

declare @userid int
declare @team int
declare @category char(1)
declare @lastcategory char(1)
set @userid = 1
set @lastcategory='B'
set @team=0

while @userid is not null 
begin

  select @category = category from users where userid = @userid
  if @category = 'A' and @lastcategory = 'B'
  begin
   set @team = @userid
  end
  update users set team = @team where userid = @userid
  set @lastcategory = @category
  select @userid = MIN(userid) from users where userid > @userid
End