5

我正在使用 SQL Server Management Studio 2008 创建查询。Reporting Services 2008 用于创建报告。

几个星期以来,我一直在努力解决这个问题,但我碰上了一堵砖墙。我希望有人能够提出解决方案,因为现在我的大脑已经变成糊状了。

我目前正在开发一个 SQL 查询,它将数据提供给 Reporting Services 报告。该报告的目的是显示我们所在县周围地区急救人员的可用性百分比。我们的想法是,在我们的 20 个地点中的每一个地点一次都应该只有一名急救人员提供掩护。

除了在一个位置的急救人员在每个掩护期的开始和结束时重叠他们的掩护之外,这一切都运行良好。

覆盖重叠示例:

| 位置 | 开始日期 | 结束日期 |
+----------+----------+---------------- -----+
| 灯芯 | 22/06/2015 09:00:00 | 22/06/2015 19:00:00 |
| 灯芯 | 22/06/2015 18:30:00 | 23/06/2015 09:00:00 |
| 灯芯 | 23/06/2015 09:00:00 | 23/06/2015 18:30:00 |
| 灯芯 | 23/06/2015 18:00:00 | 24/06/2015 09:00:00 |
+----------+----------+---------------- -----+

在一个完美的世界中,他们设置掩护的数据库不允许他们这样做,但它是一个外部开发的数据库,不允许我们对其进行这样的更改。我们也不允许创建函数、存储过程、计数表等……</p>

查询本身应该返回每个位置有急救覆盖的分钟数,然后分解为一天中的几个小时。封面上的任何重叠都不应最终增加额外的封面,而应合并。一个人一次可以上,如果他们重叠,那么它应该只算一个人很多掩护。

示例输出:

+----------+----------+---------------- -----+------------+--------------+--------+--------+- -----+----------+
| 位置 | 来自Dt | 到Dt | 时差 | 可用性 | 第N天 | 天号 | 小时 | 天数 |
+----------+----------+---------------- -----+------------+--------------+--------+--------+- -----+----------+
| 灯芯 | 22/06/2015 18:00:00 | 22/06/2015 18:59:59 | 59 | 100 | 星期一 | 1 | 18 | 0 |
| 灯芯 | 22/06/2015 18:30:00 | 22/06/2015 18:59:59 | 29 | 50 | 星期一 | 1 | 18 | 0 |
| 灯芯 | 22/06/2015 19:00:00 | 22/06/2015 19:59:59 | 59 | 100 | 星期一 | 1 | 19 | 0 |
+----------+----------+---------------- -----+------------+--------------+--------+--------+- -----+----------+

示例代码:

    DECLARE  
      @StartTime datetime,  
      @EndTime datetime, 
      @GivenDate datetime; 


 SET @GivenDate = '2015-06-22'; 
 SET @StartTime = @GivenDate + ' 00:00:00'; 
 SET @EndTime = '2015-06-23' + ' 23:59:59'; 

Declare @Sample Table
(
Location Varchar(50),
StartDate Datetime,
EndDate Datetime
)

Insert @Sample

Select
sta.location,
act.Start,
act.END

from emp,
con,
sta,
act

where 
emp.ID = con.ID
and con.location = sta.location
and SUBSTRING(sta.ident,3,2) in ('51','22')
and convert(varchar(10),act.start,111) between @GivenDate and @EndTime
and act.ACT= 18
group by sta.location,
act.Start,
act.END
order by 2

;WITH Yak (location, fromDt, toDt, maxDt,hourdiff) 
AS ( 
SELECT location, 
StartDate, 
/*check if the period of cover rolls onto the next hour */
    convert(datetime,convert(varchar(21),
    CONVERT(varchar(10),StartDate,111)+' '
    +convert(varchar(2),datepart(hour,StartDate))+':59'+':59'))
,
EndDate
,dateadd(hour,1,dateadd(hour, datediff(hour, 0, StartDate), 0))-StartDate
FROM @Sample

UNION ALL 

SELECT location, 
dateadd(second,1,toDt), 
dateadd(hour, 1, toDt),
maxDt,
hourdiff 
FROM Yak 
WHERE toDt < maxDt 
) ,

TAB1 (location, FROMDATE,TODATE1,TODATE) AS
(SELECT
location,
@StartTime,
convert(datetime,convert(varchar(21),
        CONVERT(varchar(10),@StartTime,120)+' '
        +convert(varchar(2),datepart(hour,@StartTime))+':59'+':59.999')),
@EndTime 

from @Sample

UNION ALL
SELECT 
location,
(DATEADD(hour, 1,(convert(datetime,convert(varchar(21),
        CONVERT(varchar(10),FROMDATE,120)+' '
        +convert(varchar(2),datepart(hour,FROMDATE))+':00'+':00.000')))))ToDate,
(DATEADD(hour, 1,(convert(datetime,convert(varchar(21),
        CONVERT(varchar(10),TODATE1,120)+' '
        +convert(varchar(2),datepart(hour,TODATE1))+':59'+':59.999'))))) Todate1,
TODATE
FROM TAB1 WHERE TODATE1 < TODATE
),
/*CTE Tab2 adds zero values to all possible hours between start and end dates */
TAB2 AS
(SELECT location, FROMDATE,
CASE WHEN TODATE1 > TODATE THEN TODATE ELSE TODATE1 END AS TODATE
FROM TAB1)

SELECT location, 
fromDt, 
/* Display MaxDT as start time if cover period goes into next dat */
CASE WHEN toDt > maxDt THEN maxDt ELSE toDt END AS toDt,
/* If the end date is on the next day find out the minutes between the start date and the end of the day or find out the minutes between the next day and the end date */
Case When ToDt > Maxdt then datediff(mi,fromDt,maxDt) else datediff(mi,FromDt,ToDt) end as TimeDiff,
Case When ToDt > Maxdt then round(datediff(S,fromDt,maxDt)/3600.0*100,0) else round(datediff(S,FromDt,ToDt)/3600.0*100.0,0) end as Availability,
/*Display the name of the day of the week*/
CASE WHEN toDt > maxDt THEN datename(dw,maxDt) ELSE datename(dw,fromDt) END AS DayN,
CASE WHEN toDt > maxDt THEN case when datepart(dw,maxDt)-1 = 0 then 7 else datepart(dw,maxDt)-1 end  ELSE case when datepart(dw,fromDt)-1 = 0 then 7 else  datepart(dw,fromDt)-1 END  end AS DayNo
,DATEPART(hour, fromDt) as Hour,
'0' as DayCount
FROM Yak 
where Case When ToDt > Maxdt then datediff(mi,fromDt,maxDt) else datediff(mi,FromDt,ToDt) end <> 0

group by location,fromDt,maxDt,toDt

Union all

SELECT
tab2.location, 
convert(varchar(19),Tab2.FROMDATE,120),
convert(varchar(19),Tab2.TODATE,120),
'0',
'0',
datename(dw,FromDate) DayN,
case when datepart(dw,FromDate)-1 = 0 then 7 else datepart(dw,FromDate)-1 end AS DayNo,
DATEPART(hour, fromDate) as Hour,
COUNT(distinct datename(dw,fromDate))
FROM TAB2

Where datediff(MINUTE,convert(varchar(19),Tab2.FROMDATE,120),convert(varchar(19),Tab2.TODATE,120)) > 0

group by location, TODATE, FROMDATE 

Order by 2

option (maxrecursion 0)

我尝试了以下论坛条目,但它们在我的情况下没有用: http ://forums.teradata.com/forum/general/need-help-merging-consecutive-and-overlapping-date-spans

检查时间范围重叠,守望者问题 [SQL]

计算实际停机时间,忽略日期/时间的重叠

抱歉这么长,但我想我会尽量给你提供尽可能多的细节。任何帮助将不胜感激。谢谢你。

4

1 回答 1

0

所以我想出的解决方案使用临时表,您可以轻松地将其更改为 CTE,这样您就可以避免使用存储过程。

我尝试使用窗口函数来查找重叠记录并获取最小和最大时间,问题是您有重叠链接,例如 09:00 - 09:10、09:05 - 09:15、09:11 - 09:20 , 所以从 09:00 到 09:20 的所有分钟都被覆盖了,但是几乎不可能告诉 09:00 - 09:10 与 09:11 - 09:20 相关而不递归结果直到你到达底部的链条。(希望这是有道理的)。

因此,我将所有日期范围分解为 StartDate 和 EndDate 之间的每一分钟,然后您可以使用 ROW_NUMBER() 窗口函数来捕获任何重复项,然后您可以使用它来查看有多少不同的人覆盖了同一分钟。

CREATE TABLE dbo.dates
(
Location VARCHAR(64),
StartDate DATETIME,
EndDate DATETIME
);

INSERT INTO dbo.dates VALUES
('Wick','20150622 09:00:00','20150622 19:00:00'),
('Wick','20150622 18:30:00','20150624 09:00:00'),
('Wick','20150623 09:00:00','20150623 18:30:00'),
('Wick','20150623 18:00:00','20150624 09:00:00'),
('Wick','20150630 09:00:00','20150630 09:30:00'),
('Wick','20150630 09:00:00','20150630 09:45:00'),
('Wick','20150630 09:10:00','20150630 09:25:00'),
('Wick','20150630 09:35:00','20150630 09:55:00'),
('Wick','20150630 09:57:00','20150630 10:10:00');

SELECT ROW_NUMBER() OVER (PARTITION BY Location ORDER BY StartDate) [Id],
Location,
StartDate,
EndDate
INTO dbo.overlaps
FROM dbo.dates;

SELECT TOP 10000 N=IDENTITY(INT, 1, 1)
INTO dbo.Num
FROM master.dbo.syscolumns a CROSS JOIN master.dbo.syscolumns  b;

SELECT 0 [N] INTO dbo.Numbers;

INSERT INTO dbo.Numbers SELECT * FROM dbo.Num;

SELECT  [Location]      = raw.Location,
        [WorkedDate]    = CAST([MinuteWorked] AS DATE),
        [DayN]          = DATENAME(WEEKDAY, [MinuteWorked]),
        [DayNo]         = DATEPART(WEEKDAY, [MinuteWorked]) -1,
        [Hour]          = DATEPART(HOUR, [MinuteWorked]),
        [MinutesWorked] = SUM(IIF(raw.[Minutes] = 1, 1, 0)),
        [MaxWorkers]    = MAX(raw.[Minutes])
FROM
(
SELECT
  o.Location,
  DATEADD(MINUTE, n.N, StartDate) [MinuteWorked],
  ROW_NUMBER() OVER (PARTITION BY o.Location, DATEADD(MINUTE, n.N, StartDate) ORDER BY DATEADD(MINUTE, n.N, StartDate)) [Minutes]
FROM dbo.overlaps o
INNER JOIN dbo.Numbers n ON n.N < DATEDIFF(MINUTE, StartDate, EndDate)
) raw
GROUP BY
    raw.Location,
    CAST([MinuteWorked] AS DATE),
    DATENAME(WEEKDAY, [MinuteWorked]),
    DATEPART(WEEKDAY, [MinuteWorked]) - 1,
    DATEPART(HOUR, [MinuteWorked])

这是结果的子集:

Location    WorkedDate  DayN        DayNo   Hour    MinutesWorked   MaxWorkers
Wick        2015-06-24  Wednesday   3       8       60              2
Wick        2015-06-30  Tuesday     2       9       58              3
Wick        2015-06-30  Tuesday     2       10      10              1

是小提琴

于 2018-09-29T15:50:04.877 回答