2013-02-27 2 views
2

Мне нужен запрос, чтобы назначить команды ряду пользователей. Данные выглядит следующим образом:Запрос Sql для создания команд

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 

Команда должен быть создана с помощью сортировки по идентификатору пользователя и первый идентификатор становится номер команды и часть последовательных элементов а из этой команды, как являются Б, которые следуют. Первый A после Bs начинает новую команду. Там всегда будет по крайней мере, один А и один В. Таким образом, после обновления, что данные должны выглядеть следующим образом:

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 

EDIT: Нужно добавить, что идентификатор пользователя не всегда будет увеличиваться на 1. Я редактировал примеры данных, чтобы показать, что я имею в виду. Кроме того, идентификатор команды не обязательно должен быть идентификатором первого пользователя, если они в конечном итоге группируются должным образом. Например, пользователи 1 - 4 могут быть в команде «1», пользователи 5 и 6 в команде «2» и пользователи 8,9 и 11 в команде «3»

+0

и вам нужно sql-решение без скриптов? –

+0

@SilentByte - если не со скриптами, то с чем? – LittleBobbyTables

+0

@SilentByte нет, сценарий - это то, что я ищу – Kevin

ответ

0

На самом деле я закончил работу со следующим. Он закончил все 3 миллиона + строки за полчаса.

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 
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.

+0

Попытка этого сейчас – Kevin

+0

Встаньте в икоту.См. Править для деталей. – Kevin

+0

@Kevin: вы можете использовать 'row_number' для генерации непрерывного числа в строке, ответ отредактирован – Andomar

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.

Edit:

Вы можете обновить КТР, чтобы получить это пойти:

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.

Edit:

Для больших наборов данных, которые вам нужно добавить подсказку maxrecursion запроса; Я отредактировал предыдущие запросы, чтобы показать это. Из книги онлайн:

Specifies the maximum number of recursions allowed for this query. number is a nonnegative integer between 0 and 32767. When 0 is specified, no limit is applied.

В этом случае я настроил его 0, т.е. не ограничивают рекурсии.

Query Hints.

+0

+1 Идеальная идея, используя рекурсию для обработки одной строки за раз – Andomar

+0

Идти в икоту. См. Править для деталей. – Kevin

+0

Обновлено сейчас; бесстыдно украл идею Row_Number() от @Andomar (вы должны полностью принять его ответ), но я подумал, что было бы интересно применить ее к решению CTE. –

Смежные вопросы