【问题标题】:SQL server count rows until value changesSQL 服务器计数行,直到值更改
【发布时间】:2021-06-11 05:13:33
【问题描述】:

我在 SQL Server 中计数时遇到问题。

在这样的表中:

create table mytable (name nvarchar(50), createdDate datetime)

我有一些像('John', '20160101 20:36:12'), ('Max', '20160101 20:36:12'), ('Max', '20160101 20:37:12'), ('Max', '20160101 20:38:12'), ('John', '20160101 20:38:12'), ('John', '20160101 20:39:12'), ('Max', '20160101 20:41:12')这样的行

还有更多的行,但这就是这张表的重点。我需要对行进行计数,直到名称更改,然后如果它在表中一一计算至少 2 次,则计算其他名称等。

这个表的输出应该是这样的:

Name count Min date Max date
Max 3 20160101 20:36:12 20160101 20:38:12
John 2 20160101 20:38:12 20160101 20:39:12

我该怎么做?任何建议都会很棒。示例代码也会有所帮助:)

【问题讨论】:

标签: sql sql-server count


【解决方案1】:

这是一种称为间隙和孤岛问题的问题。可能实现这个版本最简单的方法是使用 row_numbers() 与聚合的区别:

select name, count(*), min(createddate), max(createddate)
from (select t.*,
             row_number() over (order by createddate) as seqnum,
             row_number() over (partition by name order by createddate) as seqnum_2
      from mytable t
     ) t
group by name, (seqnum - seqnum_2)
having count(*) >= 2
order by min(createddate);

为什么这行得通有点难以解释。但是,我发现如果您查看子查询的结果,您会发现两个行号值的差异在名称相同的相邻行上是恒定的。

Here 是一个 dbfiddle。

【讨论】:

    【解决方案2】:

    以下查询查找所有后续区间:

    select item1.name, item1.createdDate, item2.createdDate
    from mytable item1
    join mytable item2
    on item1.name = item2.name and
       not exists (
           select 1
           from mytable inneritem
           where inneritem.name <> item1.name and
                 inneritem between item1.createdDate and item2.createdDate
       );
    

    但是,上面的查询将产生较长间隔的子集作为单独的集合,因此,为了限制上面查询的过于宽泛的结果,我们需要为每个可能的对检查以下内容:

    • 他们的名字匹配
    • 第一个早于第二个
    • 第一个和第二个之间没有与其名称不匹配的项目
    • 在第一个之前没有项目,匹配它的名称,但没有另一个名称不同的项目
    • 第二个之后没有项目,匹配它的名称,但没有另一个名称不同的项目

    解决方案

    create table mytable(name varchar(32), createdDate varchar(64));
    insert into mytable(name, createdDate) values
    ('John', '20160101 20:36:12'), 
    ('Max', '20160101 20:36:12'), 
    ('Max', '20160101 20:37:12'), 
    ('Max', '20160101 20:38:12'), 
    ('John', '20160101 20:38:12'), 
    ('John', '20160101 20:39:12'), 
    ('Max', '20160101 20:41:12');
    select item1.name, item1.createdDate, item2.createdDate
    from mytable item1
    join mytable item2
    on item1.name = item2.name and
       item1.createdDate < item2.createdDate and
       not exists (
           select 1
           from mytable inneritem
           where inneritem.name <> item1.name and
                 inneritem.createdDate > item1.createdDate and 
                 inneritem.createdDate < item2.createdDate
       ) and
       not exists (
           select 1
           from mytable inneritem
           where inneritem.name = item1.name and
                 inneritem.createdDate < item1.createdDate and
                 not exists (
                     select 1
                     from mytable innerinneritem
                     where innerinneritem.name <> item1.name and
                           innerinneritem.createdDate > inneritem.createdDate and 
                           innerinneritem.createdDate < item1.createdDate
                 )
       ) and
       not exists (
           select 1
           from mytable inneritem
           where inneritem.name = item1.name and
                 inneritem.createdDate > item2.createdDate and
                 not exists (
                     select 1
                     from mytable innerinneritem
                     where innerinneritem.name <> item1.name and
                           innerinneritem.createdDate > item2.createdDate and 
                           innerinneritem.createdDate < inneritem.createdDate
                 )
       );
    

    你可以测试的地方:http://sqlfiddle.com/#!18/7659f/7

    注意

    您有不同名称的重复日期,这可能会引起混淆。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-05-13
      • 1970-01-01
      • 2023-03-29
      • 2014-01-24
      • 2011-07-23
      • 1970-01-01
      • 2010-09-16
      相关资源
      最近更新 更多