【问题标题】:Find MIN and MAX date on a discontinuous period of time YYYYMM00在不连续的时间段 YYYYMM00 上查找 MIN 和 MAX 日期
【发布时间】:2019-05-12 14:11:12
【问题描述】:

我有以下两张表:

  • DimensionTime 是一个包含每个月的表格,按 ID 排序,采用 YYYMM00 形式
  • LogPlayer 是一个表格,其中包含与玩家和特定月份相关的一些统计信息。

我想要得到的是以下内容:

+--------+--------+----------+----------+
| Player |  Team  |  Start   |   End    |
+--------+--------+----------+----------+
| John   | Red    | 20180100 | 20180300 |
| John   | Red    | 20180600 | 20180700 |
| Luke   | Yellow | 20180100 | 20180100 |
| Luke   | Yellow | 20190100 | 20190100 |
+--------+--------+----------+----------+

我不能使用 MIN 和 MAX 函数,因为周期不连续...我该如何解决? 我已经尝试将 MIN/MAX 与 GROUP BY 结合使用,但我没有得到任何有用的信息。我在 Stackoverflow 上找不到任何问题或答案。

SELECT *
    INTO #DimensionTime
    FROM (
        SELECT 1 AS [ID], 20180100 AS [TIMEID]
        UNION ALL
        SELECT 2 AS [ID], 20180200 AS [TIMEID]
        UNION ALL
        SELECT 3 AS [ID], 20180300 AS [TIMEID]
        UNION ALL
        SELECT 4 AS [ID], 20180400 AS [TIMEID]
        UNION ALL
        SELECT 5 AS [ID], 20180500 AS [TIMEID]
        UNION ALL
        SELECT 6 AS [ID], 20180600 AS [TIMEID]
        UNION ALL
        SELECT 7 AS [ID], 20180700 AS [TIMEID]
        UNION ALL
        SELECT 8 AS [ID], 20180800 AS [TIMEID]
        UNION ALL
        SELECT 9 AS [ID], 20180900 AS [TIMEID]
        UNION ALL
        SELECT 10 AS [ID], 20181000 AS [TIMEID]
        UNION ALL
        SELECT 11 AS [ID], 20181100 AS [TIMEID]
        UNION ALL
        SELECT 12 AS [ID], 20181200 AS [TIMEID]
        UNION ALL
        SELECT 13 AS [ID], 20190100 AS [TIMEID]
        UNION ALL
        SELECT 14 AS [ID], 20190200 AS [TIMEID]
        UNION ALL
        SELECT 15 AS [ID], 20190300 AS [TIMEID]
    ) A

SELECT *
INTO #LogPlayer
FROM (
    SELECT 'John' AS [Player], 'Red' AS [Team], 20180100 AS [TIMEID]
    UNION ALL
    SELECT 'John' AS [Player], 'Red' AS [Team], 20180200 AS [TIMEID]
    UNION ALL
    SELECT 'John' AS [Player], 'Red' AS [Team], 20180300 AS [TIMEID]
    UNION ALL
    SELECT 'John' AS [Player], 'Red' AS [Team], 20180600 AS [TIMEID]
    UNION ALL
    SELECT 'John' AS [Player], 'Red' AS [Team], 20180700 AS [TIMEID]
    UNION ALL
    SELECT 'Luke' AS [Player], 'Yellow' AS [Team], 20180100 AS [TIMEID]
    UNION ALL
    SELECT 'Luke' AS [Player], 'Yellow' AS [Team], 20190100 AS [TIMEID]
) B

【问题讨论】:

  • 您是否真的在使用 SQL Server,尤其是 SQL Server 2005(已不再支持)? SQL Server 不支持yyyyMM00 日期;这是 MySQL 的事情。
  • timeID varchar 是什么数据类型?
  • @Larnu 我认为他的字段是 INT 或 BIG INT 类型,无论如何如果 SQL Server 2005 确实不是最佳方案
  • 除了奇怪的“日期”之外,这似乎是您需要对连续日期进行分组的情况。 Jeff Moden 有一个很好的方法来处理这个here。您只需要稍微修改一下以使用月份而不是日期。
  • @Lamu 是的,不幸的是我使用的是老式 SQL Server 2005 和 YYYYMM00,由于这些原因,存储为 VARCHAR。如果有帮助,我可以通过链接服务器在新的 SQL Server 2018 上运行存储过程。

标签: sql sql-server datetime sql-server-2005


【解决方案1】:

这是一种孤岛问题。即使在 SQL Server 2005 等不受支持的古老软件中也可以解决,因为该版本具有 row_number()

一个技巧是将时间 ID 转换为 真正 日期/时间。另一个技巧是通过从日期/时间值中减去连续的月份数来定义组:

select player, team, min(timeid), max(timeid)
from (select lp.*,
             row_number() over (partition by player, team order by timeid) as seqnum,
             cast(cast(timeid + 1 as varchar(255)) as datetime) as yyyymm
      from logplayer lp
     ) lp
group by player, team, dateadd(month, - seqnum, yyyymm)
order by player, team, min(timeid);

Here 是一个 dbfiddle。

【讨论】:

    【解决方案2】:

    您可以执行此类操作来查找日期运行的开始和结束。

    通过 CTE 转换为“日期”(我认为它存在于 2005 年) 然后使用 Cross Apply an EXIST 来查找日期运行的开始和结束

    你没有提供球员和球队的数据,但你可以添加到 EXISTS 中的 WHERE 条件,然后 GROUP BY - 如果需要

    ;WITH dats as (SELECT CAST(LEFT(timeid, 6) + '01' as datetime) as DT from #DimensionTime)
        select CONVERT(varchar(7),d1.DT,112) +'0'  as strt, 
               CONVERT(varchar(7),dq.dt,112) +'0' as [end] from dats d1 
            CROSS  APPLY
              (SELECT TOP 1 d3.dt from dats d3 where 
                                                d3.dt >  d1.dt 
                                                and 
                                                not exists(
                                                        select 0 from dats d4 where d4.DT = dateadd(month,1,d3.DT)
                                                          )
            ORDER BY d3.dt asc) DQ           
            where not exists(select 0 from dats d2 where d2.DT = dateadd(month,-1,d1.DT)) ;
    

    对一些样本数据进行猜测,我尝试了

    SELECT *
    INTO #DimensionTime
    FROM (
        SELECT 1 AS [ID], 20180100 AS [TIMEID], 'john' as player, 'red' as team 
        UNION ALL
        SELECT 2 AS [ID], 20180200 AS [TIMEID], 'john','red'
        UNION ALL
        SELECT 3 AS [ID], 20180300 AS [TIMEID], 'john','red'
        UNION ALL
        SELECT 4 AS [ID], 20180400 AS [TIMEID], 'john','red'
        UNION ALL
        SELECT 5 AS [ID], 20180500 AS [TIMEID], 'john','red'
        UNION ALL
        SELECT 7 AS [ID], 20180700 AS [TIMEID], 'john','red'
        UNION ALL
        SELECT 8 AS [ID], 20180800 AS [TIMEID], 'john','red'
        UNION ALL
        SELECT 9 AS [ID], 20180900 AS [TIMEID], 'john','red'
        UNION ALL
        SELECT 11 AS [ID], 20181100 AS [TIMEID], 'john','red'
        UNION ALL
        SELECT 12 AS [ID], 20181200 AS [TIMEID], 'john','red'
        UNION ALL
        SELECT 13 AS [ID], 20190100 AS [TIMEID], 'john','red'
        UNION ALL
        SELECT 14 AS [ID], 20190200 AS [TIMEID], 'john','red'
        UNION ALL
        SELECT 15 AS [ID], 20190300 AS [TIMEID], 'john','red'
        UNION ALL 
        SELECT 1 AS [ID], 20180100 AS [TIMEID], 'luke','yellow'
        UNION ALL
        SELECT 2 AS [ID], 20180200 AS [TIMEID], 'luke','yellow'
        UNION ALL
        SELECT 4 AS [ID], 20180400 AS [TIMEID], 'luke','yellow'
        UNION ALL
        SELECT 5 AS [ID], 20180500 AS [TIMEID], 'luke','yellow'
        UNION ALL
        SELECT 8 AS [ID], 20180800 AS [TIMEID], 'luke','yellow'
        UNION ALL
        SELECT 9 AS [ID], 20180900 AS [TIMEID], 'luke','yellow'
        UNION ALL
        SELECT 12 AS [ID], 20181200 AS [TIMEID], 'luke','yellow'
        UNION ALL
        SELECT 13 AS [ID], 20190100 AS [TIMEID], 'luke','yellow'
        UNION ALL
        SELECT 14 AS [ID], 20190200 AS [TIMEID], 'luke','yellow'
        UNION ALL
        SELECT 15 AS [ID], 20190300 AS [TIMEID], 'luke','yellow'
    
    
    ) A
    
    
    
    ;WITH dats as (SELECT CAST(LEFT(timeid, 6) + '01' as datetime) as DT,player,team from #DimensionTime)
        select d1.team,d1.player,
                CONVERT(varchar(7),d1.DT,112) +'0'  as strt, 
               CONVERT(varchar(7),dq.dt,112) +'0' as [end] from dats d1 
            CROSS  APPLY
              (SELECT TOP 1 d3.dt from dats d3 where 
                                                d3.dt >  d1.dt 
                                                and
                                                d3.player = d1.player
                                                and
                                                d3.team = d1.team
                                                and 
                                                not exists(
                                                        select 0 from dats d4 where d4.DT = dateadd(month,1,d3.Dt)
                                                             and  d4.team = d3.team
                                                             and d4.player = d3.player
                                                          )
            ORDER BY d3.dt asc) DQ           
            where not exists(select 0 from dats d2 where 
                        d2.player=d1.player
                        and
                        d2.team = d1.team
                        and 
                        d2.DT = dateadd(month,-1,d1.DT) and d1.team=d2.team and d1.player = d2.player ) ;
    
    drop table #DimensionTime;
    

    很抱歉我错过了最新的表格,我设计了

    ;WITH dats as (SELECT CAST(LEFT(timeid, 6) + '01' as datetime) as DT,player,team from #LogPlayer)
        select d1.team,d1.player,
                CONVERT(varchar(7),d1.DT,112) +'0'  as strt, 
               CONVERT(varchar(7),dq.dt,112) +'0' as [end] from dats d1 
            CROSS  APPLY
              (SELECT TOP 1 d3.dt from dats d3 where 
                                                d3.dt >  d1.dt 
                                                and
                                                d3.player = d1.player
                                                and
                                                d3.team = d1.team
                                                and 
                                                not exists(
                                                        select 0 from dats d4 where d4.DT = dateadd(month,1,d3.Dt)
                                                             and  d4.team = d3.team
                                                             and d4.player = d3.player
                                                          )
            ORDER BY d3.dt asc) DQ           
            where not exists(select 0 from dats d2 where 
                        d2.player=d1.player
                        and
                        d2.team = d1.team
                        and 
                        d2.DT = dateadd(month,-1,d1.DT) and d1.team=d2.team and d1.player = d2.player ) ;
    

    【讨论】:

    • 抱歉,我不明白如何运行它。我已经运行了代码,我只得到了 strt 20180100 AS [strt] 和 20190300 AS [end],这与计算 #DimensionTime 表的 MIN 和 MAX 相同
    • 那么您希望从样本数据中列出哪些日期? 20180100 是连续月份运行的开始,20190300 是运行的结束 - 如果删除中间的记录,您将获得两个序列
    • 我想要每个球员和每支球队的每个时期的开始/结束数据,就像第一个问题中的示例一样。
    • 您的样本数据中没有提到任何球队或球员 - 我会尝试为您添加一些
    • 对不起,我在这里看到了一些数据,我添加了这样做的尝试
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-08-04
    • 1970-01-01
    • 1970-01-01
    • 2021-08-05
    • 2021-05-04
    • 2014-03-11
    • 1970-01-01
    相关资源
    最近更新 更多