【问题标题】:SQL Server query to group sequential date dataSQL Server 查询对连续日期数据进行分组
【发布时间】:2012-08-30 01:02:21
【问题描述】:

今天下午我有点“大脑衰退”,所以如果有人能帮忙处理这个 mssql 查询,那就太好了。

我有一个名为“seasons”的表,其中包含三列(还有更多但与示例无关):seasonId、date、tariffId

SeasonId 是唯一键。 一个日期只能有一个关税编号,但一个关税编号可以有多个不同的日期。

例如:

seasonId  |  date      |  tariffId
----------------------------------
       1  | 1 jan 2009 |         1
       2  | 2 jan 2009 |         1
       3  | 3 jan 2009 |         2
       4  | 4 jan 2009 |         3
       5  | 5 jan 2009 |         3

我想让查询返回针对特定关税 ID 的日期序列/范围

例如使用上面的数据,它将返回以下内容:

FromDate  |  ToDate    |  TariffId
-----------------------------------
       1  | Jan 2009 2 | Jan 2009 1
       3  | Jan 2009 3 | Jan 2009 2
       4  | Jan 2009 5 | Jan 2009 3

这可能吗?

编辑 感谢您到目前为止的所有答案!我总是很惊讶你能得到这么多的回应!

但是,我的示例数据可能不够复杂,因为关税可以有 1 个或多个日期范围

seasonId  |  date      |  tariffId
----------------------------------
       1  | 1 jan 2009 |         1
       2  | 2 jan 2009 |         1
       3  | 3 jan 2009 |         2
       4  | 4 jan 2009 |         3
       5  | 5 jan 2009 |         3
       6  | 6 jan 2009 |         1
       7  | 7 jan 2009 |         1
       8  | 8 jan 2009 |         3

愿意:

FromDate   |     ToDate  |  TariffId
------------------------------------
1 Jan 2009 | 2 Jan 2009  |         1
3 Jan 2009 | 3 Jan 2009  |         2
4 Jan 2009 | 5 Jan 2009  |         3
6 Jan 2009 | 7 Jan 2009  |         1
8 Jan 2009 | 8 Jan 2009  |         3

想法?

感谢大家对此的帮助!这个网站太棒了!

【问题讨论】:

    标签: sql sql-server sql-server-2005 tsql


    【解决方案1】:

    首先是一些测试数据:

    create table seasons (seasonId int primary key
        , "date" datetime not null unique
        , tariffId int not null)
    
    insert into seasons values (1, '2009-01-01', 1)
    insert into seasons values (2, '2009-01-02', 1)
    insert into seasons values (3, '2009-01-03', 2)
    insert into seasons values (4, '2009-01-04', 3)
    insert into seasons values (5, '2009-01-05', 3)
    insert into seasons values (6, '2009-01-06', 1)
    insert into seasons values (7, '2009-01-07', 1)
    insert into seasons values (8, '2009-01-08', 3)
    -- add a tarrif with a datespan larger than 2
    insert into seasons values (9, '2009-01-09', 4)
    insert into seasons values (10, '2009-01-10', 4)
    insert into seasons values (11, '2009-01-11', 4)
    

    基于 Dave Barker 的回答,在内联视图中添加 row_number() 以便我们知道哪个是第一个最小值,哪个是第二个,等等。 (实际上由于一个日期不能有多个关税ID,所以我们不需要按关税ID进行分区。)

    SELECT MinValues.Seasonid, MinValues.Date, MaxValues.Date, MaxValues.tariffid 
    FROM (
        SELECT *, row_number() over (partition by tariffId order by "date") as RN 
          FROM [dbo].[Seasons] tbl1
         WHERE NOT EXISTS (SELECT * 
                             FROM [dbo].[Seasons] tbl2 
                            WHERE tbl1.seasonid - tbl2.seasonid = 1 
                              AND tbl1.tariffId = tbl2.tariffId)) as minValues
    JOIN (
         SELECT *, row_number() over (partition by tariffId order by "date") as RN
           FROM [dbo].[Seasons] tbl1
          WHERE NOT EXISTS (SELECT *
                              FROM [dbo].[Seasons] tbl2 
                             WHERE tbl2.seasonid - tbl1.seasonid = 1 
                               AND tbl1.tariffId = tbl2.tariffId)) as maxValues
    ON MinValues.TariffId = MaxValues.tariffId
    and MinValues.RN = MaxValues.RN
    order by MinValues.Date
    

    结果:

    1   2009-01-01 00:00:00.000 2009-01-02 00:00:00.000 1
    3   2009-01-03 00:00:00.000 2009-01-03 00:00:00.000 2
    4   2009-01-04 00:00:00.000 2009-01-05 00:00:00.000 3
    6   2009-01-06 00:00:00.000 2009-01-07 00:00:00.000 1
    8   2009-01-08 00:00:00.000 2009-01-08 00:00:00.000 3
    9   2009-01-09 00:00:00.000 2009-01-11 00:00:00.000 4
    

    【讨论】:

    • 太棒了 - 很棒的解释和代码示例。感谢您的宝贵时间
    【解决方案2】:
    SELECT min(date) as FromDate, MAX(date) as ToDate, tarifid
    FROM seasons
    GROUP BY tarifID
    

    应该这样做。

    【讨论】:

    • 如果你能想出一个这不起作用的场景,我想知道它,因为我经常使用它。如果我遗漏了什么,我想了解一下。
    • 在更详细的结果中,tarifid of 1 在结果中出现了不止一次。此示例将仅返回 1 行。
    • 嗯,显然这与原始问题不同:P
    【解决方案3】:
    SELECT min(date) as FromDate,MAX(date) as ToDate, tariffid FROM(
    select s.*,ISNULL(
    (SELECT MAX(seasonID) FROM seasons s1 WHERE s.tariffid <> s1.tariffid AND s1.seasonID < s.seasonID),0) as ranks from seasons s)r
    GROUP BY tariffID,ranks
    

    【讨论】:

      【解决方案4】:

      也许是这个?

      select min(fromdate) as FromDate, max(todate) as ToDate, tarifid
      from (
      
      select min(date) as fromdate, null as todate, tarifid
      from seasons
      group by tarifid
      
      union
      
      select null, max(date), tarifid
      from seasons
      group by tarifid
      
      ) q
      group by tarifid
      

      【讨论】:

        【解决方案5】:

        您似乎找到了连续出现的关税 ID,然后在该连续出现中找到了日期的最小值和最大值。以下适用于您的示例数据,但我怀疑最终的连接需要一些调整,因为它感觉很臭。

        SELECT MinValues.Seasonid, MinValues.Date, MaxValues.Date, MaxValues.tariffid 
        FROM (
            SELECT * 
              FROM [dbo].[Seasons] tbl1
             WHERE NOT EXISTS (SELECT * 
                                 FROM [dbo].[Seasons] tbl2 
                                WHERE tbl1.seasonid - tbl2.seasonid = 1 
                                  AND tbl1.tariffId = tbl2.tariffId)) as minValues
        JOIN (
             SELECT * 
               FROM [dbo].[Seasons] tbl1
              WHERE NOT EXISTS (SELECT *
                                  FROM [dbo].[Seasons] tbl2 
                                 WHERE tbl2.seasonid - tbl1.seasonid = 1 
                                   AND tbl1.tariffId = tbl2.tariffId)) as maxValues
        ON MinValues.TariffId = MaxValues.tariffId
        AND (MinValues.SeasonId = MaxValues.Seasonid or MinValues.SeasonId +1 = MaxValues.Seasonid)
        

        【讨论】:

        • 如果日期范围大于两天,则最终加入将不起作用。
        猜你喜欢
        • 2012-09-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-12-05
        • 1970-01-01
        • 1970-01-01
        • 2020-05-23
        • 2021-08-19
        相关资源
        最近更新 更多