Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/27.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql将日期范围拆分为周,然后根据结果计算百分比_Sql_Sql Server - Fatal编程技术网

Sql将日期范围拆分为周,然后根据结果计算百分比

Sql将日期范围拆分为周,然后根据结果计算百分比,sql,sql-server,Sql,Sql Server,我环顾四周,发现有一些与我类似的问题,我的用例略有不同。我可以管理基本的sql,但我需要一些工作 所以我的用例是我有一个折线图报告,我需要为它创建一个存储过程。我有一个签入表,如下所示。我需要做的是计算在每种地点花费的小时数,然后将每周花费的时间分开绘制折线图 我为饼图做了类似的事情。不过,这个过程并不完美 ALTER PROCEDURE [dbo].[sp_Report_TimeSpentAtBase] @startdate as DateTime, @endDate as DateTi

我环顾四周,发现有一些与我类似的问题,我的用例略有不同。我可以管理基本的sql,但我需要一些工作

所以我的用例是我有一个折线图报告,我需要为它创建一个存储过程。我有一个签入表,如下所示。我需要做的是计算在每种地点花费的小时数,然后将每周花费的时间分开绘制折线图

我为饼图做了类似的事情。不过,这个过程并不完美

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
因此,每个位置类型的每次签入的总分钟数

编辑

所以我目前的进展是

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

这是总的想法。边界检查可能需要对边缘情况进行一些调整

/*
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个方法来检查日期是否为weekend,并根据标志值返回下一个/上一个工作日

以下是程序

Alter PROCEDURE DateToInterval
-- Add the parameters for the stored procedure here
@start datetime,
@end datetime,
@interval int 
AS
BEGIN

declare @start_run as datetime
declare @end_run as datetime
declare @counter as int

--temp table to store interval 
create table #tempinterval(
StartDate datetime,
EndDate datetime,
IntervalCounter varchar(20) 
)

--update start, end date if it is weekend
set @start_run=DBTest.dbo.Check_Weekend_And_Update_With_WeekDays(@start,'add')
set @end_run=DBTest.dbo.Check_Weekend_And_Update_With_WeekDays(@start,'remove')
set @end=DBTest.dbo.Check_Weekend_And_Update_With_WeekDays(@end,'remove')
set @counter=1

--select @start_run as 'start', @end as 'end'

While(@end_run <= @end)
Begin
        --select 'loop'
        --adding interval on start date
        set @end_run = DATEADD(DAY, @interval-1, cast(@start_run as datetime))  

        if(@end_run > @end) 
            BEGIN
                set @end_run=@end
            END

        --to check if it is weekend and replace it week next weekdays
        set @end_run=DBTest.dbo.Check_Weekend_And_Update_With_WeekDays(@end_run,'add')

        insert into #tempinterval(StartDate,EndDate,IntervalCounter) values(@start_run,@end_run,'Interval '+ cast(@counter as varchar))

        set @end_run=DATEADD(DAY, 1, cast(@end_run as datetime))    

        --update end date if it is weekend
        set @end_run=DBTest.dbo.Check_Weekend_And_Update_With_WeekDays(@end_run,'add')

        set @start_run=@end_run
        set @counter =@counter + 1 

END


select * from #tempinterval

 END
 GO

虽然您提供了大量恭敬的描述,但不幸的是,这并不是对回答者最有帮助的。最好为您拥有的每个表以及select查询的结果行提供一些示例行。如果您有一个“weeks”表,其中包含每周的开始时间,会更容易吗?您可以将week表交叉连接到location表,然后对于每个week和location,您可以连接到该周内有时间的签入。他们可以计算时间,如果他们在一周内登记入住和退房,那么这可能是部分时间,然后你可以分组总结。您的“weeks”表可以是一个静态表,以使事情顺利进行,但最终您可能希望使用WITH@GeorgeMenoutis我编辑了问题并添加了一些示例数据,以及当前存储过程的结果。嘿,你能在问题中粘贴脚本而不仅仅是图片吗P@connorg98你没问题:谢谢,这正是我所需要的:
Alter PROCEDURE DateToInterval
-- Add the parameters for the stored procedure here
@start datetime,
@end datetime,
@interval int 
AS
BEGIN

declare @start_run as datetime
declare @end_run as datetime
declare @counter as int

--temp table to store interval 
create table #tempinterval(
StartDate datetime,
EndDate datetime,
IntervalCounter varchar(20) 
)

--update start, end date if it is weekend
set @start_run=DBTest.dbo.Check_Weekend_And_Update_With_WeekDays(@start,'add')
set @end_run=DBTest.dbo.Check_Weekend_And_Update_With_WeekDays(@start,'remove')
set @end=DBTest.dbo.Check_Weekend_And_Update_With_WeekDays(@end,'remove')
set @counter=1

--select @start_run as 'start', @end as 'end'

While(@end_run <= @end)
Begin
        --select 'loop'
        --adding interval on start date
        set @end_run = DATEADD(DAY, @interval-1, cast(@start_run as datetime))  

        if(@end_run > @end) 
            BEGIN
                set @end_run=@end
            END

        --to check if it is weekend and replace it week next weekdays
        set @end_run=DBTest.dbo.Check_Weekend_And_Update_With_WeekDays(@end_run,'add')

        insert into #tempinterval(StartDate,EndDate,IntervalCounter) values(@start_run,@end_run,'Interval '+ cast(@counter as varchar))

        set @end_run=DATEADD(DAY, 1, cast(@end_run as datetime))    

        --update end date if it is weekend
        set @end_run=DBTest.dbo.Check_Weekend_And_Update_With_WeekDays(@end_run,'add')

        set @start_run=@end_run
        set @counter =@counter + 1 

END


select * from #tempinterval

 END
 GO
Alter FUNCTION Check_Weekend_And_Update_With_WeekDays
(

 @date date,
 @flag varchar(10) --flag to add or removed dates to get the weekdays
 )
RETURNS datetime
 AS
 BEGIN
 -- Declare the return variable here
 DECLARE @ResultVar date


--1 (Sunday) or 7 (Saturday)
IF DATEPART(W, @date) in (1,7)
    BEGIN
        IF(DATEPART(W, @date)=1) --IF sunday
            BEGIN
                IF(@flag='add')
                    BEGIN                   
                        set @date=DATEADD(DAY, 1, cast(@date as datetime))  
                    END
                ELSE 
                    BEGIN
                        set @date=DATEADD(DAY, -2, cast(@date as datetime)) 
                    END
            END
        ELSE
            BEGIN
                IF(@flag='add') --If Saturday
                    BEGIN           
                        set @date=DATEADD(DAY, 2, cast(@date as datetime))  
                    END
                ELSE
                    BEGIN
                        set @date=DATEADD(DAY, -1, cast(@date as datetime)) 
                    END
            END             

    END

        set @ResultVar=@date


-- Return the result of the function
RETURN @ResultVar

END
GO