2

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

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”中

4

3 回答 3

4

首先,您可以用不断增加的数字标记每一行。然后您可以使用 aleft join来查找以前的用户。如果以前的用户有 category 'B',而当前有一个 category '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;

SQL Fiddle 的示例。

于 2013-02-27T17:46:25.627 回答
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 小提琴演示

编辑:

您可以更新 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 小提琴演示

编辑:

对于较大的数据集,您需要添加maxrecursion查询提示;我已经编辑了以前的查询来显示这一点。来自在线书籍:

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

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

查询提示

于 2013-02-27T17:46:55.460 回答
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
于 2013-03-01T19:46:48.023 回答