Длина подсчета последовательных повторяющихся значений для каждого идентификатора - PullRequest
0 голосов
/ 06 октября 2019

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

Для этого требуется последний столбец. Я играл с

row_number() over (partition by id, value)

, но не добился большого успеха, поскольку обведенное число (вполне предсказуемо) было вычислено как 2 вместо 1.

Пожалуйста, помогите!

Initial table & column I need to fill

Ответы [ 4 ]

3 голосов
/ 06 октября 2019

Прежде всего нам нужно определить способ упорядочения строк. Например, в ваших данных выборки невозможно гарантировать, что «первая» строка (1, 1) всегда будет отображаться перед «второй» строкой (1,0).

. Поэтому в моих данных выборкидобавлен идентификационный столбец. В вашем реальном случае детали могут быть упорядочены по идентификатору строки, столбцу даты или как-то еще, но вы должны убедиться, что строки могут быть отсортированы по уникальным критериям.

Итак, задача довольно проста:

  1. вычислить переключатель триггера - при изменении значения
  2. вычислить группы
  3. вычислить строки

Вот и все. Я использовал общее табличное выражение и оставил все столбцы, чтобы вам было легче понять логику. Вы можете разбить это на отдельные утверждения и удалить некоторые столбцы.

DECLARE @DataSource TABLE
( 
    [RowID] INT IDENTITY(1, 1)
   ,[ID]INT
   ,[value] INT
);

INSERT INTO @DataSource ([ID], [value])
VALUES (1, 1)
      ,(1, 0)
      ,(1, 0)
      ,(1, 1)
      ,(1, 1)
      ,(1, 1)
      --
      ,(2, 0)
      ,(2, 1)
      ,(2, 0)
      ,(2, 0);

WITH DataSourceWithSwitch AS
(
    SELECT *
          ,IIF(LAG([value]) OVER (PARTITION BY [ID] ORDER BY [RowID]) = [value], 0, 1) AS [Switch]
    FROM @DataSource
), DataSourceWithGroup AS
(
    SELECT *
          ,SUM([Switch]) OVER (PARTITION BY [ID] ORDER BY [RowID]) AS [Group]
    FROM DataSourceWithSwitch
)
SELECT *
      ,ROW_NUMBER() OVER (PARTITION BY [ID], [Group] ORDER BY [RowID]) AS [GroupRowID]
FROM DataSourceWithGroup
ORDER BY [RowID];

enter image description here

2 голосов
/ 06 октября 2019

Требуются результаты, которые зависят от фактического порядка данных в источнике данных. В SQL вы работаете с отношениями, иногда с упорядоченным набором строк отношений. Ваш желаемый конечный результат не является четко определенным с точки зрения SQL, если только вы не введете дополнительный столбец в исходной таблице, по которому упорядочены ваши данные (например, автоинкремент или некоторый столбец отметки времени).

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

1 голос
/ 06 октября 2019

Один из способов решить эту проблему может быть с помощью рекурсивного CTE:

create table #tmp (i int identity,id int, value int, rn int);
insert into #tmp (id,value) VALUES
  (1,1),(1,0),(1,0),(1,1),(1,1),(1,1),
  (2,0),(2,1),(2,0),(2,0);
WITH numbered AS (
 SELECT i,id,value, 1 seq FROM #tmp WHERE i=1 UNION ALL
 SELECT a.i,a.id,a.value, CASE WHEN a.id=b.id AND a.value=b.value THEN b.seq+1 ELSE 1 END
 FROM #tmp a INNER JOIN numbered b ON a.i=b.i+1
)
SELECT * FROM numbered -- OPTION (MAXRECURSION 1000)

Это вернет следующее:

i   id  value   seq
1   1   1       1
2   1   0       1
3   1   0       2
4   1   1       1
5   1   1       2
6   1   1       3
7   2   0       1
8   2   1       1
9   2   0       1
10  2   0       2

Смотрите мое небольшое демо здесь: https://rextester.com/ZZEIU93657

Необходимым условием работы CTE является таблица последовательности (например, таблица с столбцом identity в ней) в качестве источника. В моем примере я ввел столбец i для этого. В качестве отправной точки мне нужно найти первую запись исходной таблицы. В моем случае это была запись с i=1.

. Для более длинной исходной таблицы вы можете столкнуться с ошибкой предела рекурсии, поскольку по умолчанию для MAXRECURSION установлено значение 100. В этом случае вы должны раскомментировать OPTION. установка позади моего SELECT пункта выше. Вы можете либо установить более высокое значение (как показано на рисунке), либо полностью отключить его, установив его на 0.

0 голосов
/ 06 октября 2019

ИМХО, это проще сделать с помощью курсора и цикла.

может быть, есть способ выполнить работу с selfjoin

declare @t table (id int, val int)
insert into @t (id, val)

             select 1 as id, 1 as val
   union all select 1, 0
   union all select 1, 0
   union all select 1, 1
   union all select 1, 1
   union all select 1, 1

;with cte1 (id , val , num ) as
(
    select id, val, row_number() over (ORDER BY (SELECT 1)) as num from @t
)
, cte2 (id, val, num, N) as
(
   select id, val, num, 1 from cte1 where num = 1
   union all
   select t1.id, t1.val, t1.num, 
    case when t1.id=t2.id and t1.val=t2.val then t2.N + 1 else 1 end 
   from cte1 t1 inner join cte2 t2 on t1.num = t2.num + 1 where t1.num > 1
)

select * from cte2
...