SQL Server计算值在多个日期范围之间出现的次数,并与前几周进行比较

时间:2017-08-16 17:11:07

标签: sql-server-2012 sql-server-2014

我首先在

问了一个类似的问题

SQL Server to count how many times a value appears between multiple date ranges

并且不确定我是否应该发布新问题或修改原始帖子。虽然它是在原始帖子的基础上建立的,但我认为它有足够的不同来呈现一个新帖子 - 请告知这是否是正确的事情。

我没有得到一些运作良好的解决方案,但不幸的是我向我发出了一个警告,我需要根据其他标准进行一些修改而无法解决。

首先,这里再次是一些样本数据和预期输出

|   Time_Stamp    | Emp_ID  | Balance | Hours |
|-----------------|---------|---------|-------|
| 7/16/2017 19:40 | 3140340 |    2250 |  37.5 |
| 7/16/2017 19:40 | 2000950 |    4050 |  67.5 |
| 7/16/2017 19:40 | 3118410 |     400 |   6.7 |
| 7/16/2017 19:40 |  311840 |   11700 |   195 |
| 7/23/2017 21:19 | 3140340 |    2250 |  37.5 |
| 7/23/2017 21:19 | 2000950 |    4050 |  67.5 |
| 7/23/2017 21:19 | 3118410 |     800 |  13.3 |
| 7/23/2017 21:19 | 3124160 |     450 |   7.5 |
| 7/23/2017 21:19 |  311840 |     400 |   6.7 |
| 7/30/2017 7:00  | 3140340 |    2250 |  37.5 |
| 7/30/2017 7:00  | 2000950 |     400 |   6.7 |
| 7/30/2017 7:00  | 3118410 |    1200 |    20 |
| 7/30/2017 7:00  |  311840 |     700 |  11.7 |
| 8/6/2017 12:00  | 3140340 |     444 |   7.4 |
| 8/6/2017 12:00  | 3118410 |     444 |   7.4 |
| 8/6/2017 12:00  | 3124160 |      90 |   1.5 |
| 8/6/2017 12:00  |  311840 |     325 |   5.4 |
| 8/13/2017 12:00 | 3140340 |     900 |    15 |
| 8/13/2017 12:00 | 3118410 |    1350 |  22.5 |
| 8/13/2017 12:00 | 3124160 |      90 |   1.5 |
| 8/13/2017 12:00 |  311840 |    1700 |  28.3 |

预期输出如下

|         | 16-Jul | 23-Jul | 30-Jul | 6-Aug | 13-Aug |
|---------|--------|--------|--------|-------|--------|
| emp_id  | wk1    | wk2    | wk3    | wk4   | wk5    |
| 3140340 | 1      | 2      | 3      | 0     | 1      |
| 2000950 | 1      | 2      | 0      | 0     | 0      |
| 3118410 | 0      | 1      | 2      | 0     | 1      |
| 311840  | 1      | 0      | 1      | 0     | 1      |
| 3124160 | 0      | 1      | 0      | 1     | 2      |

一个重要的注意事项 - 遗憾的是,余额(分钟)的数据类型在varchar中,我需要将其转换为小时数除以60.我使用以下内容

ROUND(CONVERT(varchar(50),CONVERT(float,([BALANCE]/convert(float,60.0)))),2) AS [Hours]

说到我现在需要完成的是,如果每周的余额少于7.5小时,那么只需输入0(零)。

如果> = 7.5小时则计数为1.如果连续几周> = 7.5小时 - 例如连续2周,那么第一周的计数为1,第二周的计数为2如果第3周低于7.5小时,则为0。

如前所述,原始问题和解决方案可以在以下位置找到 SQL Server to count how many times a value appears between multiple date ranges

对原始问题非常有效的两种解决方案是

;WITH 
weekcounts AS (
SELECT Time_Stamp, Emp_ID, DATEPART(week, Time_Stamp) AS int_week FROM sampleData
)

,counts AS (
SELECT Emp_ID, int_week, 1 AS int_count
FROM weekcounts
UNION ALL
SELECT weekcounts.Emp_ID, weekcounts.int_week, 1 AS int_count
FROM weekcounts
    INNER JOIN counts
        ON weekcounts.Emp_ID = counts.Emp_ID
        AND (weekcounts.int_week - 1) = counts.int_week
)

,countsagg AS (
SELECT Emp_ID, int_week, SUM(int_count) AS int_count
FROM counts
GROUP BY Emp_ID, int_week
)

SELECT * FROM countsagg
PIVOT (MAX(int_count) FOR int_week IN ([29],[30],[31],[32],[33])) piv

; with wk_nbrs as
(
    --recursive CTE that generates the week numbers.
    -- 7/23 thru 7/29 is Week 1
    select cast('2017-07-23' as date) as wk_bgn
    , cast('2017-07-29' as date) as wk_end
    , 1 as wk_nbr
    union all
    select dateadd(d,7,fw.wk_bgn) as wk_bgn
    , dateadd(d,7,fw.wk_end) as wk_end
    , fw.wk_nbr + 1 as wk_nbr
    from wk_nbrs as fw
    where 1=1
    and fw.wk_nbr < 100
)
, emp_wk_cnt as
(
    --Getting the running total count of emp_id by week
    select a.emp_id
    , b.wk_nbr
    , count(*) over (partition by a.emp_id order by b.wk_nbr asc) as emp_wk_cnt
    from @emp_ts as a
    inner join wk_nbrs as b on cast(a.time_stamp as date) between b.wk_bgn and b.wk_end
    group by a.emp_id
    , b.wk_nbr  
)
--pivoting the results out to final expected output
select post.emp_id
, post.wk2
, post.wk3
, post.wk4
from (
select a.emp_id
, 'wk' + cast(a.wk_nbr as varchar(10)) as wk_nbr
, a.emp_wk_cnt
from emp_wk_cnt as a
) as pre
pivot (sum(pre.emp_wk_cnt) 
    for pre.wk_nbr in 
        ([wk2],[wk3],[wk4])
) post
order by post.emp_id

非常感谢您对此提供的任何其他帮助。

提前致谢。

1 个答案:

答案 0 :(得分:1)

我认为这可能会给你你想要的东西:

;WITH 
weekcounts AS (
    SELECT Time_Stamp, Emp_ID, [Balance], ROUND(CONVERT(FLOAT,[Balance]) / 60.0,2) AS [Hours], DATEPART(week, Time_Stamp) AS int_week FROM newSampleData
)

,counts AS (
    SELECT Emp_ID, int_week, 1 AS int_count
    FROM weekcounts
    WHERE ([Hours] >= 7.5)
    UNION ALL
    SELECT weekcounts.Emp_ID, weekcounts.int_week, 1 AS int_count
    FROM weekcounts
        INNER JOIN counts
            ON weekcounts.Emp_ID = counts.Emp_ID
            AND (weekcounts.int_week - 1) = counts.int_week
            AND ([Hours] >= 7.5)
)

,countsagg AS (
    SELECT Emp_ID, int_week, SUM(int_count) AS int_count
    FROM counts
    GROUP BY Emp_ID, int_week
)

SELECT Emp_ID, 
       ISNULL([29],0) AS [week 29],
       ISNULL([30],0) AS [week 30],
       ISNULL([31],0) AS [week 31],
       ISNULL([32],0) AS [week 32],
       ISNULL([33],0) AS [week 33] 
FROM countsagg
PIVOT (MAX(int_count) FOR int_week IN ([29],[30],[31],[32],[33])) piv

此外,如果Balance是一个varchar,那么您提供的代码中所需的转换数量超过了所需数量。这将使用较少的代码提供数小时的结果:

ROUND(CONVERT(FLOAT,[Balance]) / 60.0,2)