【问题标题】:SQL Server range countSQL Server 范围计数
【发布时间】:2017-03-20 14:30:21
【问题描述】:

我有一个 SQL Server 表,其中一列代表时间,而另一列代表一个 ID,如下所示:

    Datum                     SomeID 
2017-01-01 07:44:57.840         1   
2017-01-02 07:45:10.093         2   
2017-01-02 07:45:21.557         3    
2017-01-03 09:07:21.253         2  
2017-01-05 09:07:42.680         1 
2017-01-06 09:07:49.007         5 

我正在寻找一个 SQL 查询或存储过程,它将时间单位作为输入(例如“YY”、“MM”、“DD”、“HH”)并返回一个表,其中每个表都有唯一的 ID 计数时间间隔。如果没有找到该间隔的记录,则计数应为零。

在这种情况下,假设'DD'作为时间单位,结果应该是:

Time interval      Count
2017-01-01           1
2017-01-02           2
2017-01-03           1
2017-01-04           0
2017-01-05           1
2017-01-06           1

【问题讨论】:

    标签: sql sql-server count


    【解决方案1】:

    可能最简单的方法是动态SQL:

    declare @timeunit nvarchar(255);
    declare @sql nvarchar(max);
    
    set timeunit = N'hh';
    
    set @sql = '
    select dateadd(@timeunit, datediff(@timeunit, 0, datum), 0) as dt, count(*)
    from t
    group by dateadd(@timeunit, datediff(@timeunit, 0, datum), 0) 
    ';
    
    set @sql = replace(@sql, '@timeunit', @timeunit);
    
    exec sp_executesql @sql;
    

    我不喜欢将用户输入放入这样的查询中。不过,您可以轻松地检查它的有效值:

    (case when @timeunit in ('year', 'yy', 'month', 'mm',  . . .) then . . .
     end)
    

    【讨论】:

    • 您还需要根据 OP 要求将 0 行变为存在。
    • 感谢您的代码!有什么建议我可以从 Python 中调用它吗?以及如何处理有问题的0行?
    【解决方案2】:

    我经常使用表值函数来创建动态日期/时间范围。一个计数/日历表也可以解决问题。但是,该功能提供了更多的灵活性。参数为日期范围、日期部分和增量。

    我还应该注意您的特定问题,我使用 Format()。此功能有一些很棒的功能,但它的性能未知。

    示例:

    Declare @YourTable table (Datum datetime,SomeID int)
    Insert Into @YourTable values
    ('2017-01-01 07:44:57.840',1),
    ('2017-01-02 07:45:10.093',2),   
    ('2017-01-02 07:45:21.557',3),    
    ('2017-01-03 09:07:21.253',2),  
    ('2017-01-05 09:07:42.680',1), 
    ('2017-01-06 09:07:49.007',5)
    
    Declare @DispFormat varchar(10)='yyyy-MM-dd'   --<< Standard Format() options
    Declare @DatePart   varchar(10)='DD'           --<< YY,MM,DD,HH,MI,SS
    Declare @Increment  int        = 1             --<< Set Increment
    
    Declare @DateR1 datetime
    Declare @DateR2 datetime
    Select @DateR1=min(Datum),@DateR2=max(Datum) from @YourTable  --<< Can be set manually if desired
    
    Select A.TimeInterval 
          ,Count        = count(Distinct SomeID)
     From  (Select *,TimeInterval = format(RetVal,@DispFormat) From [dbo].[udf-Range-Date](@DateR1,@DateR2,@DatePart,@Increment)) A
     Left  Join @YourTable B on format(A.RetVal,@DispFormat) = format(B.Datum,@DispFormat)
     Group By  A.TimeInterval 
    

    退货

    TimeInterval    Count
    2017-01-01      1
    2017-01-02      2
    2017-01-03      1
    2017-01-04      0
    2017-01-05      1
    2017-01-06      1
    

    UDF(如果有兴趣)

    CREATE FUNCTION [dbo].[udf-Range-Date] (@R1 datetime,@R2 datetime,@Part varchar(10),@Incr int)
    Returns Table
    Return (
        with cte0(M)   As (Select 1+Case @Part When 'YY' then DateDiff(YY,@R1,@R2)/@Incr When 'QQ' then DateDiff(QQ,@R1,@R2)/@Incr When 'MM' then DateDiff(MM,@R1,@R2)/@Incr When 'WK' then DateDiff(WK,@R1,@R2)/@Incr When 'DD' then DateDiff(DD,@R1,@R2)/@Incr When 'HH' then DateDiff(HH,@R1,@R2)/@Incr When 'MI' then DateDiff(MI,@R1,@R2)/@Incr When 'SS' then DateDiff(SS,@R1,@R2)/@Incr End),
             cte1(N)   As (Select 1 From (Values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N(N)),
             cte2(N)   As (Select Top (Select M from cte0) Row_Number() over (Order By (Select NULL)) From cte1 a, cte1 b, cte1 c, cte1 d, cte1 e, cte1 f, cte1 g, cte1 h ),
             cte3(N,D) As (Select 0,@R1 Union All Select N,Case @Part When 'YY' then DateAdd(YY, N*@Incr, @R1) When 'QQ' then DateAdd(QQ, N*@Incr, @R1) When 'MM' then DateAdd(MM, N*@Incr, @R1) When 'WK' then DateAdd(WK, N*@Incr, @R1) When 'DD' then DateAdd(DD, N*@Incr, @R1) When 'HH' then DateAdd(HH, N*@Incr, @R1) When 'MI' then DateAdd(MI, N*@Incr, @R1) When 'SS' then DateAdd(SS, N*@Incr, @R1) End From cte2 )
    
        Select RetSeq = N+1
              ,RetVal = D 
         From  cte3,cte0 
         Where D<=@R2
    )
    /*
    Max 100 million observations -- Date Parts YY QQ MM WK DD HH MI SS
    Syntax:
    Select * from [dbo].[udf-Range-Date]('2016-10-01','2020-10-01','YY',1) 
    Select * from [dbo].[udf-Range-Date]('2016-01-01','2017-01-01','MM',1) 
    */
    

    只是为了好玩...试试:

    Declare @DispFormat varchar(10)='HH:00'
    Declare @DatePart   varchar(10)='HH'   
    Declare @Increment  int        = 1     
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-07-22
      • 2013-12-15
      • 1970-01-01
      • 1970-01-01
      • 2021-07-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多