Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/87.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

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/25.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_Tsql_Cursor - Fatal编程技术网

Sql 方法),但从循环到基于集合的逻辑的改变的好处远远大于成本

Sql 方法),但从循环到基于集合的逻辑的改变的好处远远大于成本,sql,sql-server,tsql,cursor,Sql,Sql Server,Tsql,Cursor,以下是部分解决方案(无法处理不同的日期): 您可以使用公共表表达式(CTE)或数字表生成日期和小时的列表,然后汇总每个小时消耗的资源。将时段汇总到四分之一小时窗口的相关答案如下。无论如何,事实并非如此。提示:使用软件和版本标记数据库问题,例如,sql-server-2012,因为功能不同。@HABO注意,递归CTE使用隐藏的RBAR。这是一篇由Jeff Moden本人撰写的文章,他是RBAR的创始人,旨在获取更多信息!这对样本数据集起作用,看起来要快得多。 Call_ID StartDate

以下是部分解决方案(无法处理不同的日期):


您可以使用公共表表达式(CTE)或数字表生成日期和小时的列表,然后汇总每个小时消耗的资源。将时段汇总到四分之一小时窗口的相关答案如下。无论如何,事实并非如此。提示:使用软件和版本标记数据库问题,例如,
sql-server-2012
,因为功能不同。@HABO注意,递归CTE使用隐藏的RBAR。这是一篇由Jeff Moden本人撰写的文章,他是RBAR的创始人,旨在获取更多信息!这对样本数据集起作用,看起来要快得多。
Call_ID   StartDateTime     EndDateTime  
1         3/15/2014 11:25   3/15/2014 14:01  
2         3/15/2014 11:50   3/15/2014 13:10  
3         3/15/2014 12:05   3/15/2014 12:55  
4         3/15/2014 13:04   3/15/2014 15:02  
5         3/15/2014 13:15   3/15/2014 14:22  
6         3/15/2014 14:35   3/15/2014 15:18  
7         3/15/2014 15:10   3/15/2014 15:29  

Date        Hour    CallMinutes  
3/15/2014   11       45  
3/15/2014   12       170  
3/15/2014   13       171  
3/15/2014   14       108  
3/15/2014   15       39  
/* create & fill the Inputs table */  
CREATE TABLE PhoneCalls(  
    Call_ID         int IDENTITY(1,1) NOT NULL,  
    StartDateTime   datetime NOT NULL,  
    EndDateTime     datetime NOT NULL,  
 CONSTRAINT PK_PhoneCalls PRIMARY KEY CLUSTERED ( Call_ID ASC) ON [PRIMARY]  
) ON [PRIMARY];  

INSERT INTO PhoneCalls (StartDateTime, EndDateTime) VALUES ('3/15/2014 11:25', '3/15/2014 14:01');  
INSERT INTO PhoneCalls (StartDateTime, EndDateTime) VALUES ('3/15/2014 11:50', '3/15/2014 13:10');  
INSERT INTO PhoneCalls (StartDateTime, EndDateTime) VALUES ('3/15/2014 12:05','3/15/2014 12:55');  
INSERT INTO PhoneCalls (StartDateTime, EndDateTime) VALUES ('3/15/2014 13:04','3/15/2014 15:02');  
INSERT INTO PhoneCalls (StartDateTime, EndDateTime) VALUES ('3/15/2014 13:15','3/15/2014 14:22');  
INSERT INTO PhoneCalls (StartDateTime, EndDateTime) VALUES ('3/15/2014 14:35','3/15/2014 15:18');  
INSERT INTO PhoneCalls (StartDateTime, EndDateTime) VALUES ('3/15/2014 15:10','3/15/2014 15:29');  

/* Create the Temp table to hold the intermediate results */
Create Table #HourlyCallTimes (
    Call_ID     int NOT NULL,
    CallDate    date NOT NULL,
    CallHour    smallint NOT NULL,
    CallTime    int NOT NULL);

/* Determine Hourly totals of CallTime using a cursor */
SET NOCOUNT ON;
DECLARE @Call_ID int, @StartDt datetime, @EndDt datetime, @CallLength int, @Hour int, @CallTime int;
declare CallsCursor cursor for
    select Call_ID, StartDateTime, EndDateTime, DATEDIFF(minute, StartDateTime, EndDateTime) as CallLength
    FROM PhoneCalls
open CallsCursor
fetch next from CallsCursor into @Call_ID, @StartDt, @EndDt, @CallLength
while @@FETCH_STATUS = 0
begin
    /* make sure that the call was at least a minute long */
    IF (@CallLength > 0.01)
    BEGIN
        -- Record the call time for the first partial hour
        SET @Hour = DATEPART(Hour, @StartDt)
        SET @CallTime = 60 - DATEPART(Minute, @StartDt)
        if (@CallLength < @CallTime) -- check if this us the last (partial) hour
            SET @CallTime = @CallLength
        INSERT INTO #HourlyCallTimes (Call_ID, CallDate, CallHour, CallTime)
            VALUES (@Call_ID, CAST(@StartDt as DATE), @Hour, @CallTime)

        -- move to the beginning of the next hour
        SET @StartDt = DATEADD(HOUR,@Hour+1,CAST(CAST(@StartDt as DATE) as DateTime))
        SET @CallLength = @CallLength - @CallTime
        While @StartDt < @EndDt
        BEGIN
            SET @Hour = DATEPART(Hour, @StartDt)
            SET @CallTime = 60;
            if (@CallLength < @CallTime)  -- check if this is the last (partial) hour
                SET @CallTime = @CallLength
        INSERT INTO #HourlyCallTimes (Call_ID, CallDate, CallHour, CallTime)
            VALUES (@Call_ID, CAST(@StartDt as DATE), @Hour, @CallTime)
            -- move to the beginning of the next hour
            SET @StartDt = DATEADD(Hour,1,@StartDt)     
            SET @CallLength = @CallLength - @CallTime
        END
    END
    fetch next from CallsCursor into @Call_ID, @StartDt, @EndDt, @CallLength
END
close CallsCursor;
deallocate CallsCursor;

/* here's the final results table */
Create Table HourlyCallTotals (
    CallDate    date NOT NULL,
    CallHour    smallint NOT NULL,
    CallTime    int NOT NULL);

Insert into HourlyCallTotals (CallDate, CallHour, CallTime)
    select CallDate, CallHour, SUM(CallTime) from #HourlyCallTimes group by CallDate, CallHour;

DROP TABLE #HourlyCallTimes;
DROP TABLE PhoneCalls;

SELECT * FROM HourlyCallTotals;
DROP TABLE HourlyCallTotals;

/* expected Results */
/*  CallDate    CallHour    CallTime
    3/15/2014       11          45
    3/15/2014       12          170
    3/15/2014       13          171
    3/15/2014       14          108
    3/15/2014       15          39 */
;WITH cte_break AS
(
SELECT 
CONVERT(DATE,StartDateTime) AS CallDate, 
DATEPART(HOUR,StartDateTime) AS CallHour, 
CASE WHEN DATEDIFF(MINUTE,StartDateTime, EndDateTime) < 60 AND DATEPART(HOUR, StartDateTime) = DATEPART(HOUR, EndDateTime) THEN DATEDIFF(MINUTE,StartDateTime, EndDateTime) 
ELSE (60 - DATEPART(MINUTE,StartDateTime)) END AS CallTime,
DATEADD(minute, CASE WHEN DATEDIFF(MINUTE,StartDateTime, EndDateTime) < 60 THEN DATEDIFF(MINUTE,StartDateTime, EndDateTime) ELSE (60 - DATEPART(MINUTE,StartDateTime)) END , StartDateTime) AS CalcStartTime,
EndDateTime
FROM dbo.PhoneCalls

UNION ALL
SELECT 
CONVERT(DATE,CalcStartTime) AS CallDate, 
DATEPART(HOUR,CalcStartTime) AS CallHour, 
CASE WHEN DATEDIFF(MINUTE,CalcStartTime, EndDateTime) < 60 AND DATEPART(HOUR, CalcStartTime) = DATEPART(HOUR, cte_break.EndDateTime) THEN DATEDIFF(MINUTE,CalcStartTime, EndDateTime) 
ELSE (60 - DATEPART(MINUTE,CalcStartTime)) END AS CallTime,
DATEADD(minute, CASE WHEN DATEDIFF(MINUTE,CalcStartTime, EndDateTime) < 60 THEN DATEDIFF(MINUTE,CalcStartTime, EndDateTime) ELSE (60 - DATEPART(MINUTE,CalcStartTime)) END , CalcStartTime) AS CalcStartTime,
EndDateTime
FROM cte_break
WHERE CalcStartTime < EndDateTime

)
SELECT CallDate, CallHour, SUM(CallTime) AS CallTime FROM cte_break
GROUP BY CallDate, CallHour
ORDER BY CallDate, CallHour
ID Hour0 Hour1 Hour2 Hour3 ...
1 0 0 4 0
2 0 0 3 0
3 0 1 2 0
...
DECLARE @maxHour INT, @minHour INT, @disHours INT

SELECT  @minHour = MIN(DATEPART(HOUR, StartDateTime)),
    @maxHour = MAX(DATEPART(HOUR, EndDateTime)),
    @disHours = @maxHour - @minHour + 1
FROM    PhoneCalls

-- Filling out a range from @minHour to @maxHour
SELECT TOP (@disHours) @minHour -1 + ROW_NUMBER() OVER (ORDER BY [object_id]) AS CallHour
INTO    #hours
FROM    sys.all_objects 
ORDER BY CallHour

-- Count the per call per hour call time
SELECT  CallHour,
    CASE
        WHEN DATEPART(HOUR, StartDateTime) < CallHour AND DATEPART(HOUR, EndDateTime) > CallHour THEN 60
        WHEN DATEPART(HOUR, StartDateTime) = CallHour AND DATEPART(HOUR, EndDateTime) = CallHour THEN DATEPART(MINUTE, EndDateTime) - DATEPART(MINUTE, StartDateTime)
        WHEN DATEPART(HOUR, StartDateTime) < CallHour AND DATEPART(HOUR, EndDateTime) = CallHour THEN DATEPART(MINUTE, EndDateTime)
        WHEN DATEPART(HOUR, StartDateTime) = CallHour AND DATEPART(HOUR, EndDateTime) > CallHour THEN 60 - DATEPART(MINUTE, StartDateTime)
        ELSE 0
    END     AS CallTimePerCallPerHour
INTO    #cross
FROM    PhoneCalls
    CROSS JOIN #hours

-- The final result
SELECT  CallHour,
    SUM(CallTimePerCallPerHour) AS CallTime
FROM    #cross
WHERE   CallTimePerCallPerHour > 0  -- To left out hours with 0 CallTime in the result
GROUP BY CallHour

DROP TABLE #hours
DROP TABLE #cross
3/15/2016 23:49 - 3/16/2016 00:05 =>
3/15/2016 23:49 - 3/15/2016 23:60 // instead of 24:00 so we don't need to count hour 24
3/16/2016 00:00 - 3/16/2016 00:05