Sql 计算每个id的连续重复值的长度

Sql 计算每个id的连续重复值的长度,sql,sql-server,tsql,presto,Sql,Sql Server,Tsql,Presto,我有一个表,如屏幕截图前两列所示,我需要创建一个类似于最后一列的列。我试图计算每个id的每个连续值序列的长度 为此,最后一列是必需的。我和他一起玩 row_number() over (partition by id, value) 但并没有取得太大的成功,因为圆圈数字可以预测为2而不是1 请帮忙 IMHO,使用光标和循环更容易做到这一点 也许有一种方法可以通过selfjoin完成这项工作 declare @t table (id int, val int) insert into @t (i

我有一个表,如屏幕截图前两列所示,我需要创建一个类似于最后一列的列。我试图计算每个id的每个连续值序列的长度

为此,最后一列是必需的。我和他一起玩

row_number() over (partition by id, value)
但并没有取得太大的成功,因为圆圈数字可以预测为2而不是1

请帮忙


IMHO,使用光标和循环更容易做到这一点

也许有一种方法可以通过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

您需要的结果取决于数据源中的实际数据顺序。在SQL中,您操作关系,有时操作关系行的有序集合。除非在源表中引入一个额外的列,在该列上对数据进行排序(例如,自动递增或某个时间戳列),否则您所需的最终结果在SQL方面没有很好的定义


注意:这回答了原始问题,没有考虑注释中提到的附加时间戳列。我不会更新我的答案,因为已经有一个可接受的答案。

首先,我们需要有一种方法来定义行的排序方式。例如,在示例数据中,无法确保“第一”行1,1始终显示在“第二”行1,0之前

这就是为什么我在示例数据中添加了一个标识列。在实际情况中,详细信息可以按行ID、日期列或其他内容排序,但您需要确保可以通过唯一的条件对行进行排序

因此,任务非常简单:

计算触发开关-值更改时 计算组 计算行数 就这样。为了便于您理解逻辑,我使用了公共表表达式并保留了所有列。您可以在单独的语句中自由地打断它,并删除一些列

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];

解决此问题的一种方法是通过递归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
在这里查看我的小演示:

CTE工作的先决条件是顺序表e。G一种表,其中有一个标识列作为源。在我的示例中,我为此介绍了专栏I。作为起点,我需要找到源表的第一个条目。在我的例子中,这是i=1的条目


对于较长的源表,您可能会遇到递归限制错误,因为MAXRECURSION的默认值为100。在这种情况下,您应该取消注释上面我的SELECT子句后面的选项设置。您可以将其设置为更高的值,如图所示,也可以通过将其设置为0将其完全关闭。

您是对的。假设存在一列时间戳,并且数据按每个id的升序排序