【问题标题】:SQL: Group By on Consecutive RecordsSQL:按连续记录分组
【发布时间】:2010-08-04 23:34:47
【问题描述】:

一个有点棘手的 SQL 问题(我们正在运行 SQL server 2000)。

我有下表,StoreCount -

WeekEndDate    StoreCount
2010-07-25     359
2010-07-18     359
2010-07-11     358
2010-07-04     358
2010-06-27     358
2010-06-20     358
2010-06-13     358
2010-06-06     359
2010-05-30     360
2010-05-23     360
2010-05-16     360

我想把它变成下面的输出 -

StartDate    EndDate       StoreCount
2010-07-18   2010-07-25    359
2010-06-13   2010-07-11    358
2010-06-06   2010-06-06    359
2010-05-16   2010-05-30    360

如您所见,我想对商店计数进行分组,仅当它们一起按顺序运行时。

【问题讨论】:

  • #359,你怎么知道六月的日期是独立的,和下一个七月(18日)的日期没有联系?
  • 因为记录是按日期降序排列的
  • @CraigS:不够好 - 没有规则,我们无法隔离记录。
  • 嗨 SoftwareGeek - 这是一个真实的例子。很多时候,在软件开发中,你没有重新设计数据库等的选项,你只需要使用那里的东西。
  • @OMGPonies 似乎对下面的帕特里克来说已经足够好了;)

标签: sql sql-server tsql sql-server-2000


【解决方案1】:

试一试,只是它可能具有 SS2k 中不可用的语法。它实际上是在 Oracle 上编写的,因为我没有那个版本的 SS。唯一的问题可能是 select 的 select ...(我使用 SS2k 已经有一段时间了,所以很难记住当时哪些功能不可用。)

select min(weekenddate) as start_date, end_date, storecount
from (
select s1.weekenddate
     , (select max(weekenddate)
          from store_stats s2
         where s2.storecount = s1.storecount
           and not exists (select null
                             from store_stats s3
                            where s3.weekenddate < s2.weekenddate
                              and s3.weekenddate > s1.weekenddate
                              and s3.storecount <> s1.storecount)
       ) as end_date
     , s1.storecount
from store_stats s1
) result
group by end_date, storecount
order by 1 desc


START_DATE END_DATE   STORECOUNT
---------- ---------- ----------
2010-07-18 2010-07-25        359
2010-06-13 2010-07-11        358
2010-06-06 2010-06-06        359
2010-05-16 2010-05-30        360

【讨论】:

  • Patrick - 我认为这很好,只是您需要为 FROM 查询的结果添加别名。 group by end_date, storecount 上方的行,从 ) 更改为 ) Result。我的一项资格;我也没有 SQL Server 2000。
  • Brilliant Patrick - 正如 bobs 指出的那样,我需要添加的只是别名,而且效果很好。干得好。
  • 每次都能吸引我。 Oracle 不关心这些别名。 SQL Server 可以。感谢您的修复。
  • Patrick - 我知道这已经很老了,但你说你的解决方案是为 SQL 2000 设计的......你会使用最新版本以不同的方式来做吗?
  • @StephenHolt - 抱歉,我不经常查看该站点的消息。哇,没有什么比回顾我 5 年前所做的事情更有趣的了。 ;) 我确实整理了这个查询的滞后/领先版本(在 Oracle 12c 中,我没有方便的 SQL Server)来看看它会如何比较以及在 20,000 行样本上;它的运行速度比原来快 4 倍(14 秒对 57 秒)。我应该编辑我的原件还是作为新的发布?
【解决方案2】:

使用光标。我不知道如何在 sql2k 中使用查询。

DECLARE @w datetime
DECLARE @s int
DECLARE @prev_s int
DECLARE @start_w datetime
DECLARE @end_w datetime

CREATE TABLE #zz(start datetime, [end] datetime, StoreCount int)

DECLARE a_cursor CURSOR
FOR SELECT WeekEndDate, StoreCount FROM Line ORDER BY WeekEndDate DESC, StoreCount
OPEN a_cursor
FETCH NEXT FROM a_cursor INTO @w, @s
WHILE @@FETCH_STATUS = 0
BEGIN

    IF @end_w IS NULL 
    BEGIN

        SET @end_w = @w
        SET @start_w = @w     
        SET @prev_s = @s
    END 
    ELSE IF @prev_s <> @s
    BEGIN
       INSERT INTO #zz values(@start_w,  @end_w, @prev_s)

       SET @end_w = @w  
       SET @start_w = @w  
       SET @prev_s = @s 
    END ELSE
        SET @start_w = @w 

    FETCH NEXT FROM a_cursor INTO @w, @s
END
-- add last one
INSERT INTO #zz values(@start_w, @end_w, @prev_s)

CLOSE a_cursor
DEALLOCATE a_cursor

SELECT * FROM #zz ORDER BY 1 DESC
DROP TABLE #zz

【讨论】:

  • 查看我的 cmets 问题 - #359 不会有正确的值。
【解决方案3】:

好的,这就是我的尝试。

DECLARE @curDate DATETIME = (SELECT MIN(WeekEndDate) FROM Table_1);
    DECLARE @curCount INT = (SELECT StoreCount FROM Table_1 WHERE WeekEndDate = @curDate);
    DECLARE @sDate DATETIME = GETDATE()
    DECLARE @eDate DATETIME = 0


    WHILE @eDate < (SELECT MAX(WeekEndDate) FROM Table_1)
    BEGIN
        SELECT @sDate = (SELECT WeekEndDate AS StartDate FROM Table_1 WHERE WeekEndDate = @curDate) -- SELECT START DATE

        -- NOW GET THE END DATE IN THIS GROUP
        DECLARE @d1 DATETIME = @curDate
        DECLARE @d2 DATETIME = @curDate
        DECLARE @loop INT = 1
        WHILE @loop = 1
            BEGIN
                IF ((SELECT StoreCount FROM Table_1 WHERE WeekEndDate = @d1) <> @curCount OR @d1 = (SELECT MAX(WeekEndDate) FROM Table_1)) BEGIN
                    SELECT @eDate = (SELECT TOP(1) WeekEndDate FROM Table_1 WHERE StoreCount = @curCount AND WeekEndDate = @d2 ORDER BY WeekEndDate DESC)
                    SELECT @loop = 0 END
                ELSE BEGIN
                    SELECT @d2 = @d1 
                    SELECT @d1 = (SELECT TOP(1) WeekEndDate FROM Table_1 WHERE WeekEndDate > @d1 ORDER BY WeekEndDate) END
            END


        SELECT @sDate AS StartDate, @eDate AS EndDate, @curCount AS StoreCount   -- DO QHATEVER YOU NEED TO DO WITH THE RECORDS HERE

        SELECT TOP(1) @curDate = WeekEndDate, @curCount = StoreCount
        FROM Table_1
        WHERE WeekEndDate > @eDate
        GROUP BY WeekEndDate, StoreCount
        ORDER BY WeekEndDate ASC

    END

【讨论】:

    【解决方案4】:

    我不确定如何解释这一点,但对于给定的小数据集,它似乎给出了预期的结果。本质上,它会检测系列中值发生变化的点。

    查询计划我没看过,可能会很痛苦。

    在 Sybase 服务器上尝试过,因此语法应该与 SQL Server 2K 兼容。

    SELECT  x.StartDate
            , MIN( y.EndDate ) AS EndDate
            , x.StoreCount
    FROM
    ( SELECT
            wed1.WeekEndDate AS StartDate
            , wed1.StoreCount
    FROM
            wed wed1
    LEFT JOIN
            wed wed2
    ON      wed1.WeekEndDate = DATEADD( DAY, 7, wed2.WeekEndDate )
    WHERE
            wed1.StoreCount != ISNULL( wed2.StoreCount, wed1.StoreCount - 1 )
    ) x,
    ( SELECT
            wed1.WeekEndDate AS EndDate
    FROM
            wed wed1
    LEFT JOIN
            wed wed2
    ON      wed1.WeekEndDate = DATEADD( DAY, -7, wed2.WeekEndDate )
    WHERE
            wed1.StoreCount != ISNULL( wed2.StoreCount, wed1.StoreCount - 1 )
    ) y
    WHERE
            y.EndDate >= x.StartDate
    GROUP BY
            x.StartDate
    HAVING
            x.StartDate = MIN( x.StartDate )
    ORDER BY
            1 DESC
    
    StartDate    EndDate      StoreCount
    ------------ ------------ -----------
     Jul 18 2010  Jul 25 2010         359
     Jun 13 2010  Jul 11 2010         358
     Jun  6 2010  Jun  6 2010         359
     May 16 2010  May 30 2010         360
    

    【讨论】:

      【解决方案5】:

      试试这个简单的解决方案:

      create table x  (weekEndDate char(10), storeCount int);
      insert into x values
      ('2010-07-25',359),
      ('2010-07-18',359),
      ('2010-07-11',358),
      ('2010-07-04',358),
      ('2010-06-27',358),
      ('2010-06-20',358),
      ('2010-06-13',358),
      ('2010-06-06',359),
      ('2010-05-30',360),
      ('2010-05-23',360),
      ('2010-05-16',360);
      select min(weekenddate) as startdate, max(weekenddate) as enddate, min(storecount) as storecount 
      from 
      (select weekenddate, storecount, concat(row_number() over (order by weekenddate) -row_number() over (partition by storecount order by weekenddate),'|',storecount) as groupkey from x) w
      group by groupkey order by startdate desc;
      

      sqlfiddle

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-09-15
        • 2020-05-23
        • 1970-01-01
        • 2018-04-09
        • 1970-01-01
        • 2017-05-20
        • 1970-01-01
        • 2022-01-08
        相关资源
        最近更新 更多