Подсчет последовательных повторяющихся записей с помощью SQL - PullRequest
3 голосов
/ 10 августа 2009

У меня есть вопрос анализа данных, который я мог бы легко решить с помощью некоторого T-SQL или некоторых сценариев, но мне было интересно, есть ли умное решение SQL.Проблема в том, что он немного портится с предположением о независимости строки SQL.

У меня есть таблица, которая состоит из пар имя-значение, связанных с пользователем и упорядоченных по отправке, например:

ID      USERID  VARIABLE        VALUE   SUBMITTED
3115    2287    votech05    2   2009-02-02 15:34:00
3116    2287    comcol05    1   2009-02-02 15:34:00
3117    2287    fouryr05    1   2009-02-02 15:35:00
3118    2287    none05          2   2009-02-02 15:35:00
3119    2287    ocol1_05    2   2009-02-02 15:44:00
3120    2287    disnone         2   2009-02-02 15:45:00
3121    2287    dissense    2   2009-02-02 15:49:00
3122    2287    dismobil    3   2009-02-02 15:51:00
3123    2287    dislearn    3   2009-02-02 15:51:00
3124    2287    disment         3   2009-02-02 15:52:00
3125    2287    disother    2   2009-02-02 15:55:00
3126    2287    disrefus    7   2009-02-02 15:58:00

Я бы хотел иметь возможность определить значение и количество наибольшей группы идентичных значений (когда данные упорядочены по первичному ключу идентификатора).Итак, для приведенного выше примера, поскольку у меня есть четыре значения = 2, появляющиеся в последовательности, и только три значения = 3, я бы хотел сообщить:

USERID     VALUE      COUNT
2287       2          4

для данного пользователя.

Опять же, это можно было бы сделать довольно быстро, используя другие инструменты, но, поскольку набор данных довольно большой (около 75 миллионов записей) и часто меняется, было бы неплохо решить эту проблему с помощью запроса.Я работаю с SQL Server 2005.

Ответы [ 3 ]

3 голосов
/ 10 августа 2009

(отредактировано после комментария)

Вы можете сделать это, назначив «головной» номер каждой группе последовательных значений. После этого вы выбираете номер головки для каждой строки и производите агрегацию на голову.

Вот пример с CTE для удобства чтения:

WITH
OrderedTable as (
    select value, rownr = row_number() over (order by userid, id)
    from YourTable
    where userid = 2287
),
Heads as (
    select cur.rownr, CurValue = cur.value
    ,   headnr = row_number() over (order by cur.rownr)
    from OrderedTable cur
    left join OrderedTable prev on cur.rownr = prev.rownr+1 
    where IsNull(prev.value,-1) != cur.value
),
ValuesWithHead as (
    select value
    ,   HeadNr = (select max(headnr) 
                from Heads 
                where Heads.rownr <= data.rownr)
    from OrderedTable data
)
select Value, [Count] = count(*)
from ValuesWithHead
group by HeadNr, value
order by count(*) desc

Будет выведено:

Value   Count
2       4
3       3
1       2
2       1
2       1
7       1

Используйте «top 1», чтобы выбрать только первую строку.

Вот мой запрос для создания тестовых данных:

create table YourTable (
    id int primary key,
    userid int,
    variable varchar(25),
    value int
)
insert into YourTable (id, userid, variable, value) values (3115, 2287, 'votech05', 2)
insert into YourTable (id, userid, variable, value) values (3116, 2287, 'comcol05', 1)
insert into YourTable (id, userid, variable, value) values (3117, 2287, 'fouryr05', 1)
insert into YourTable (id, userid, variable, value) values (3118, 2287, 'none05', 2)
insert into YourTable (id, userid, variable, value) values (3119, 2287, 'ocol1_05', 2)
insert into YourTable (id, userid, variable, value) values (3120, 2287, 'disnone', 2)
insert into YourTable (id, userid, variable, value) values (3121, 2287, 'dissense', 2)
insert into YourTable (id, userid, variable, value) values (3122, 2287, 'dismobil', 3)
insert into YourTable (id, userid, variable, value) values (3123, 2287, 'dislearn', 3)
insert into YourTable (id, userid, variable, value) values (3124, 2287, 'disment', 3)
insert into YourTable (id, userid, variable, value) values (3125, 2287, 'disother', 2)
insert into YourTable (id, userid, variable, value) values (3126, 2287, 'disrefus', 7)
2 голосов
/ 10 августа 2009

Это может быть одной из тех проблем, которые лучше всего решить с помощью курсоров. Дайте это попробовать. Это должно быть близко, но это не проверено, поскольку вы не предоставили операторам CREATE TABLE и INSERT примеры данных, чтобы упростить это.

declare @userid int
set @userid = 2287;
declare C cursor fast_forward for
select VALUE from T
where USERID = @userid
order by ID;

declare @value int, @prevvalue int;
declare @runcount int, @runlongest int;
set @runlongest = 0;
declare @valuelongest int;
open C;
fetch next from C into @value;
while @@fetch_status = 0 begin
  if @value = @prevvalue set @runcount = @runcount + 1 else set @runcount = 1;
  if @runcount > @runlongest begin
    set @runlongest = @runcount;
    set @valuelongest = @value;
  end;
  set @prevvalue = @value;
  fetch next from C into @value;
end;
select @userid as USERID, @valuelongest as VALUE, @runlongest as [COUNT];

close C;
deallocate C;

Это не будет быстрым с 75M строками, но, вероятно, тоже не будет слишком медленным. Если ваши прогоны очень длинные, и у вас есть правильные индексы, вы можете добиться большего, если нумеровать строки с помощью row_number во временной таблице, а затем использовать цикл WHILE, который перебирает цикл за раз. Дайте мне знать, если вы считаете, что на это стоит обратить внимание (и, если можете, опубликуйте операторы CREATE TABLE и INSERT с примерами данных).

0 голосов
/ 11 августа 2009

без тестирования я думаю, что должно работать следующее:

Row_number () over (разделение по идентификатору пользователя, порядок значений по идентификатору)

как только это будет сделано, просто выберите тот, который имеет самый высокий row_nunber

Пожалуйста, дайте мне знать, если это сработало !!

Спасибо, Эди

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...