SQL将日期范围分成几周,然后计算结果百分比

时间:2018-07-06 09:46:31

标签: sql sql-server

我环顾四周,我也有类似的问题,购买我的用例略有不同。我可以管理基本的SQL,但可以使用一些工作。

所以我的用例是我有一个折线图报告,需要为其创建存储的proc。我有一个签到表,如下所示。我需要做的是计算每种位置类型所花费的时间,然后每周将其单独用于折线图。

enter image description here

我对饼图做了类似的事情。程序虽然不漂亮。

ALTER PROCEDURE [dbo].[sp_Report_TimeSpentAtBase]   
@startdate as DateTime,
@endDate as DateTime = null,
@teamID as uniqueidentifier = null,
@userID as uniqueidentifier = null

AS
BEGIN   
    SELECT 
    LocationType,
    Sum(DATEDIFF(minute, InAt, OutAt)) Seconds
    FROM CheckIns
        INNER JOIN Locations ON Locations.Id = CheckIns.Location_ID
        INNER JOIN [System].[Users] ON CheckIns.User_Id = [System].[Users].Id
    WHERE 
    DATEPART(dw, InAt) NOT IN (1, 7) AND
    InAt >= @startdate AND
    OutAt IS NOT NULL AND
    DATEPART(dw, OutAt) NOT IN (1, 7) AND
    (@teamID IS NULL OR [System].[Users].Team_ID = @teamID) AND 
    (@userID IS NULL OR [System].[Users].ID = @userID) AND
    (@endDate IS NULL OR OutAt <= @endDate)
    GROUP BY LocationType 
END
GO

因此,为清楚起见,用户签入位置,我将其记录在签入表中。然后,我使用签入/签出时间来计算他们在该位置所花费的时间。我使用的折线图需要这种格式。

enter image description here

因此,理想情况下,我应该为每个位置类型都添加一个TimeSpentAtBaseRow,然后每个星期分配一个系列。

我不希望有人为我写这篇文章,我可以朝着正确的方向前进。感谢您的帮助或指导。

每个表格的数据示例:

enter image description here

位置类型只是一个枚举

enter image description here

和签到表

enter image description here

最后,现有存储过程的结果如下:

LocationType | Minutes
----------------------
Base         | 100
Hospital     | 200

所以每种位置类型的每次签到的总分钟数。

编辑

所以我目前的进展是

WITH dates as 
(
    select number, DATEADD(day, number, '20170101') as dt
    from master..spt_values
    where number between 0 and 1000 AND TYPE ='P'
)
SELECT 
    l.LocationType,
    d.dt,
    Sum(DATEDIFF(minute, InAt, OutAt)) as mins
FROM Checkins ci
    INNER JOIN Locations l ON l.Id = ci.Location_ID
JOIN dates d
    on d.dt between ci.InAt and ci.OutAt
GROUP BY
    d.dt, l.LocationType

2 个答案:

答案 0 :(得分:1)

这是一般想法。对于边缘情况,边界检查可能需要一些调整。

/*
Set up data
*/
declare @startDate datetime = '07/01/2018'; -- first day you want included
declare @endDate datetime = '07/15/2018'; -- day AFTER the last you want included

declare @location table (locationId int, locationName varchar(100));
declare @log table (logId int, locationId int, checkIn datetime, checkOut datetime);

insert @location values 
    (1, 'Location 1'), 
    (2, 'Location 2');

insert @log values 
    (1, 1, '07/07/2018 20:00:00', '07/08/2018 06:00:00'), 
    (2, 1, '07/08/2018 20:00:00', '07/09/2018 06:00:00'), 
    (3, 1, '07/09/2018 20:00:00', '07/10/2018 06:00:00')
    ;

/* 
Summary by location
*/
select  loc.locationId ,
        loc.locationName ,
        SUM(DATEDIFF(MINUTE, log.checkIn, log.checkOut)) as minutes
from    @location loc
inner join @log log on log.locationId = loc.locationId
group by loc.locationId ,
        loc.locationName
;

/*
Summary by location and week
*/
with
    -- List of weeks.  There are other ways to do this.
    weeks as (
        select  @startDate as startDate ,
                dateadd(week, 1, @startDate) as endDate
        union all
        select  endDate /*as startDate*/ ,
                dateadd(week, 1, endDate) /* as endDate*/
        from    weeks
        where   endDate < @endDate
    ) ,
    -- Determine how much time each log entry is in each week.
    logWeeks as (
        select  log.locationId ,
                w.startDate as week ,
                datediff(
                    minute ,
                    case when log.checkIn >= w.startDate then log.checkIn else w.startDate end ,
                    case when log.checkOut <= w.endDate then log.checkOut else w.endDate end
                ) as minutes
        from    @log log
        inner join weeks w
                on log.checkIn < w.endDate
                and log.checkOut >= w.startDate
    )
-- Summarize.
select  loc.locationId ,
        loc.locationName ,
        lw.week ,
        sum(lw.minutes)
from    @location loc
inner join logWeeks lw
        on lw.locationId = loc.locationId
group by loc.locationId ,
        loc.locationName ,
        lw.week
order by loc.locationId ,
        lw.week
;

答案 1 :(得分:0)

下面是我为要求创建的解决方案,该解决方案将日期范围分为给定的时间间隔(不包括周末)。

我创建了1个将日期范围划分为给定间隔的过程。 另外,创建了一种方法来检查日期是否为周末,并根据标志值返回下一个/上一个工作日。

下面是过程

<footnotes>
    <footnote id="F1">content1</footnote>
    <footnote id="F2">content2</footnote>
    <footnote id="F3">content3</footnote>
    <footnote id="F4">content4</footnote>
</footnotes>

检查周末并将其转换为工作日的方法

import csv
from bs4 import BeautifulSoup

with open('output.csv', 'w', newline='') as outfile:
    writer = csv.writer(outfile, )
    soup = BeautifulSoup(doc, 'htmparser') #Let's say doc has the html.
    Footnotes = soup.select('footnotes')
    Footnotes = '/'.join(Footnotes)

writer.writerow(Footnotes.text)