如何创建仅由前一行的值定义的行号?

Posted

技术标签:

【中文标题】如何创建仅由前一行的值定义的行号?【英文标题】:How can I create row numbers defined only by the previous row's value? 【发布时间】:2021-11-18 17:09:54 【问题描述】:

这是以前由一个非常旧的 T-SQL 脚本中的游标完成的任务,我现在必须摆脱它。对于按日期排序的表中的一个人,我有一个值表示一个序列正在开始,然后继续,然后当一个新的开始时(表示旧的已经结束)。我无法弄清楚如何让这些序列中的每一个都具有行号。几年前,我在 R 代码库中有类似的东西,我使用过 RLE,但这让我很难过。我需要从中得到:

ID  STATUS              DATE        A       B
1   START               2000-01-01  1       1
1   CONTINUATION_A&B    2000-01-02  NULL    NULL
1   CONTINUATION_A&B    2000-01-03  NULL    NULL
1   START               2000-01-04  1       1
1   START               2000-01-05  1       1
1   CONTINUATION_A      2000-01-06  NULL    NULL
1   CONTINUATION_A      2000-01-07  NULL    NULL

到这里:

ID  STATUS              DATE        A       B
1   START               2000-01-01  1       1
1   CONTINUATION_A&B    2000-01-02  2       2
1   CONTINUATION_A&B    2000-01-03  3       3
1   START               2000-01-04  1       1
1   START               2000-01-05  1       1
1   CONTINUATION_A      2000-01-06  2       1
1   CONTINUATION_A      2000-01-07  3       1

提前致谢。

【问题讨论】:

您使用的是什么数据库和版本?确切的品种和版本绝对会对这里的最佳答案产生重大影响。例如,mysql 5.7 及更早版本和 Sql Server 2012 及更早版本将需要与更高版本截然不同的代码。 我将在 Spark SQL 中编写解决方案,它是 ANSI SQL 2003 IIRC 的子集。 那么你很幸运:看起来 Spark SQL 支持 the row_number() function 这个 A/B 列编号应该如何工作? @CClarke 我想通了。但是最后一行在 A 列中的值是错误的。 【参考方案1】:
with A as (
    select *,
        count(case when status  = 'START' then 1 end) over (order by "date") as grp
    from T
)
select *,
    count(case when status in ('START', 'CONTINUATION_A', 'CONTINUATION_A&B') then 1 end)
        over (partition by grp order by "date") as A,
    count(case when status in ('START', 'CONTINUATION_A&B') then 1 end)
        over (partition by grp order by "date") as B
from A;

https://dbfiddle.uk/?rdbms=sqlserver_2014&fiddle=225a1e37236c18fbb7bdbb76d7ad93dc

这假设计数器总是从 1 开始。如有必要,可以使用以下表达式进行调整:

min(A) over (partition by grp) - 1 /* offset for A */
min(B) over (partition by grp) - 1 /* offset for B */

【讨论】:

谢谢!我从来没有在 over 子句中使用过 count 或 sum - 我必须正确学习这项技术。 @CClarke 它们在这里基本相同,所以不确定我为什么混合它们。深入了解分析函数绝对很方便。【参考方案2】:

不是答案,但对于最终回答问题很重要,而且评论太长(因此是社区 wiki)。


我看到了:

在按日期排序的表格中

...但我在问题中看不到任何日期。示例数据中的日期字段在哪里?我们至少需要知道它的名字才能给你好的代码。

让您深入了解的一件事是表格从不具有任何固有或自然的顺序。虽然主键/聚集索引或插入顺序可能看起来像一个自然的表顺序,但有很多事情会弄乱这一点,除非您在代码中明确说明记录的顺序,否则数据库可以免费为您提供以它认为方便的任何顺序生成。也就是说,如果没有完全确定性的ORDER BY 子句,则同一查询的结果顺序可以并且确实会随时更改,具体取决于当前正在运行的其他查询以访问相同数据或什么页面或索引已在内存中。

这意味着我们需要能够引用表中的一个字段来强制执行所需的排序...我们需要了解该日期字段以编写正确的 SQL 语句。

【讨论】:

有一个非常精确的 order by 语句,它为我提供了我在原始问题中提供的更长版本。所以我想我应该说假设这些数据是 order by 语句的结果 - 我怎样才能创建我需要的列? @CClarke 这里的目标是使用窗口函数,所以我们仍然需要日期字段作为数据的一部分。 对不起 - 当然 - 我已经添加了日期列。谢谢。

以上是关于如何创建仅由前一行的值定义的行号?的主要内容,如果未能解决你的问题,请参考以下文章

如何计算每一行的值?

如何在vba中得到当前单元格的行号和列号

上一行的值有值,前一些行没有值

VBA中如何取得行号和列号如何选定这一范围的值

如果另一列中的值是唯一的,那么如何在SQL中放置一个显示1的列,如果它是重复的则为0?

delphi中如何在dbgrid中主动添加序号?求赞助!