【问题标题】:T-SQL Calculate time between changing status per hourT-SQL计算每小时改变状态之间的时间
【发布时间】:2019-12-30 13:55:36
【问题描述】:

编辑: 我改变了我的例子,让它更简单。第一个引号是源表的样子,第二个引号是结果的样子。


大家好,

我有多个只发送变化状态的停车场。 当汽车到达停车场时它会发送一个“1”,然后在汽车再次离开之前它不会发送任何东西。在那一刻,停车发送一个“0”。我需要在很长一段时间内进行分析,因此看到每小时左右的时间量不会得到太多行(按分钟比较),这将是非常棒的。

数据看起来像这样(根据要求,我将其减少为停车 ID 10,仅是 19.12 的最后一条记录。以及 20.12 的记录。):

+------------+------------------+--------+-------------+
| Parking-ID | DateTime         | Status | Comment     |
+------------+------------------+--------+-------------+
| 10         | 20.12.2019 16:35 | 0      | Car left    |
+------------+------------------+--------+-------------+
| 10         | 20.12.2019 08:22 | 1      | Car arrived |
+------------+------------------+--------+-------------+
| 10         | 19.12.2019 22:47 | 0      | Car left    |
+------------+------------------+--------+-------------+

现在为了不让我太容易,在“免费”和“被占用”状态旁边还有一个温暖状态。汽车离开后 1 小时停车应标记为“暖”,因为有些汽车必须在几分钟内快速进出,此时间范围应显示为“暖”。

为了不获取太多行(例如每分钟),如果可以每小时获取摘要,我将不胜感激。对于我的分析,我应该能够看到每天停车的时间、温暖的时间以及免费的时间。

所以结果应该是这样的(对于 Parking-ID 10 和 20.12.2019):

+------------+------------------+--------+----------+---------+
| Parking-ID | DateTime         | Status | Duration | Comment |
+------------+------------------+--------+----------+---------+
| 10         | 20.12.2019 23:00 | 0      | 1.00     | Free    |
+------------+------------------+--------+----------+---------+
| 10         | 20.12.2019 22:00 | 0      | 1.00     | Free    |
+------------+------------------+--------+----------+---------+
| 10         | 20.12.2019 21:00 | 0      | 1.00     | Free    |
+------------+------------------+--------+----------+---------+
| 10         | 20.12.2019 20:00 | 0      | 1.00     | Free    |
+------------+------------------+--------+----------+---------+
| 10         | 20.12.2019 19:00 | 0      | 1.00     | Free    |
+------------+------------------+--------+----------+---------+
| 10         | 20.12.2019 18:00 | 0      | 1.00     | Free    |
+------------+------------------+--------+----------+---------+
| 10         | 20.12.2019 17:00 | 0      | 0.42     | Free    |
+------------+------------------+--------+----------+---------+
| 10         | 20.12.2019 17:00 | 2      | 0.58     | Warm    |
+------------+------------------+--------+----------+---------+
| 10         | 20.12.2019 16:00 | 2      | 0.42     | Warm    |
+------------+------------------+--------+----------+---------+
| 10         | 20.12.2019 16:00 | 1      | 0.58     | Taken   |
+------------+------------------+--------+----------+---------+
| 10         | 20.12.2019 15:00 | 1      | 1.00     | Taken   |
+------------+------------------+--------+----------+---------+
| 10         | 20.12.2019 14:00 | 1      | 1.00     | Taken   |
+------------+------------------+--------+----------+---------+
| 10         | 20.12.2019 13:00 | 1      | 1.00     | Taken   |
+------------+------------------+--------+----------+---------+
| 10         | 20.12.2019 12:00 | 1      | 1.00     | Taken   |
+------------+------------------+--------+----------+---------+
| 10         | 20.12.2019 11:00 | 1      | 1.00     | Taken   |
+------------+------------------+--------+----------+---------+
| 10         | 20.12.2019 10:00 | 1      | 1.00     | Taken   |
+------------+------------------+--------+----------+---------+
| 10         | 20.12.2019 09:00 | 1      | 1.00     | Taken   |
+------------+------------------+--------+----------+---------+
| 10         | 20.12.2019 08:00 | 1      | 0.63     | Taken   |
+------------+------------------+--------+----------+---------+
| 10         | 20.12.2019 08:00 | 0      | 0.37     | Free    |
+------------+------------------+--------+----------+---------+
| 10         | 20.12.2019 07:00 | 0      | 1.00     | Free    |
+------------+------------------+--------+----------+---------+
| 10         | 20.12.2019 06:00 | 0      | 1.00     | Free    |
+------------+------------------+--------+----------+---------+
| 10         | 20.12.2019 05:00 | 0      | 1.00     | Free    |
+------------+------------------+--------+----------+---------+
| 10         | 20.12.2019 04:00 | 0      | 1.00     | Free    |
+------------+------------------+--------+----------+---------+
| 10         | 20.12.2019 03:00 | 0      | 1.00     | Free    |
+------------+------------------+--------+----------+---------+
| 10         | 20.12.2019 02:00 | 0      | 1.00     | Free    |
+------------+------------------+--------+----------+---------+
| 10         | 20.12.2019 01:00 | 0      | 1.00     | Free    |
+------------+------------------+--------+----------+---------+
| 10         | 20.12.2019 00:00 | 0      | 1.00     | Free    |
+------------+------------------+--------+----------+---------+

有人有好的方法吗?我已经搜索并尝试过,但找不到可行的方法。

谢谢你和最好的问候

【问题讨论】:

  • IMO,Output is not as per given input.你能否根据给定的输出快速更改输入,以便清楚。 (Parking-ID 10 和 20.12.2019 的输入)
  • 你能补充一下预期的结果吗
  • 我更改了示例。 @KumarHarsh 20.12 的输入。并输出 20.12。现在应该匹配
  • @KrishnaMuppalla 第二张表是预期的结果。
  • @denzel,请检查我的答案,最重要的是清除疑问,很好。

标签: sql-server tsql


【解决方案1】:

First,如果你交叉检查,你的持续时间输出仍然是错误的。 例如 20.12.2019 08:00 应该是 22.00 和 38.00。清除这个?

Secondly,20.12.2019 17:00 的两行不清楚。为什么它会包含 2 行?也清除这个。

Create Calendar table in whatever way you want.

CREATE TABLE [dbo].[CalendarDate](
[Dates] [datetime2](0) NOT NULL
PRIMARY KEY CLUSTERED 
(
 [Dates] ASC
)
) ON [PRIMARY]
GO

insert into [CalendarDate] with(tablock)
select top (100000) 
dateadd(day,ROW_NUMBER()over(order by (select null))
,'1950-01-01 00:00:00') 
from sys.objects a, sys.objects b, sys.objects c

然后也创建数字表

 -- Real or #temp your wish
create Table #Number(Hrs int)
insert into #Number (Hrs)
select top 24 ROW_NUMBER()over(order by number)-1 
from master..spt_values

您的表格示例数据。我已将停车状态保存在单独的表格中,请遵循规范化。

-- your real table
create table #Parking( ParkingID int, ParkingDateTime Datetime2(0),ParkingStatus tinyint  )
insert into #Parking values(10,'2019-12-20 16:35',0),(10,'2019-12-20 08:22',1)
,(10,'2019-12-19 22:47',0)

-- It should be your real table
create table #ParkingStatus( ParkingStatus tinyint,StatusName varchar(50)  )
insert into #ParkingStatus values(0,'Car left')
,(1,'Car arrived'),(2,'Free'),(3,'Taken')
,(4,'Warm')

脚本,

declare @From Datetime2(0)='2019-12-20'
declare @To Datetime2(0)=dateadd(second,-1,dateadd(day,1,@From))

 -- Put require data in #temp table,since it will be use many times
create table #ParkingTemp(ParkingID int,ParkingDateTime Datetime2(0)
,ParkingDate Date,ParkingStatus tinyint )

insert into #ParkingTemp (ParkingID,ParkingDateTime
,ParkingDate,ParkingStatus)
select P.ParkingID,ParkingDateTime
,p.ParkingDateTime 
,ParkingStatus
 from #Parking P
where ParkingDateTime>=@From
and ParkingDateTime<=@To



;With CTE as
(
select ParkingID,ParkingDateTime ,count(*)+1 SplitCount
,ParkingStatus as InitialStatus
from #ParkingTemp
group by ParkingID,ParkingDateTime,ParkingStatus
)
, DistinctIDCTE as
(
select distinct ParkingID
 from #ParkingTemp

)
, CTE1 as 
(
select Dates 
,dateadd(hour,hrs,Dates)ReportDateTime
,ParkingID
from [CalendarDate],#Number N,DistinctIDCTE
where dates>=@From and Dates<=@To
),
CTE2 as
(
select c.ParkingID
,dateadd(minute,-datepart(minute,ParkingDateTime),ParkingDateTime) ParkingDate
,ParkingDateTime,hrs as rownum,InitialStatus
 from CTE C
cross apply(select hrs from #Number N where c.SplitCount>n.Hrs)ca
)
,CTE3 as
(
select parkingid,ParkingDateTime as FromDatetime 
,ToDatetime
from #ParkingTemp C
cross apply(select top 1 ParkingDateTime as ToDatetime 
from #ParkingTemp C1 where c.ParkingID=c1.ParkingID
and  c1.ParkingStatus=0 and 
c1.ParkingDateTime>c.ParkingDateTime 
order by c1.ParkingDateTime  )c1
where ParkingStatus=1
)
,CTE4 as
(
select c.ParkingID,c.ReportDateTime
from CTE1 C
outer apply(select top 1 FromDatetime ,ToDatetime
from  CTE3 c1 where c.ParkingID=c1.ParkingID
 and (ReportDateTime>= FromDatetime and ReportDateTime<=ToDatetime))ca
  )

  --select * from CTE2
 ,CTE5 as
 (
  select c4.ParkingID,c4.ReportDateTime
  ,case when rownum=0 and InitialStatus=1 then 2
  when rownum=1 and InitialStatus=1 then 3
  when rownum=0 and InitialStatus=0 then 4
  when rownum=1 and InitialStatus=0 then 3
  else 2 end as ParkingStatusid
  ,case when rownum=0  then datediff(minute,ReportDateTime,ParkingDateTime)
  when rownum=1 then 60- datepart(minute,ParkingDateTime)
  else 1.00 end Duration
  ,ParkingDateTime
  ,rownum,InitialStatus
  from CTE4 c4
  left join CTE2 c2 on c4.ParkingID=c2.ParkingID and c2.ParkingDate =c4.ReportDateTime

   )

   select c5.ParkingID,c5.ReportDateTime,c5.ParkingStatusid
   ,Duration,PS.StatusName AS Comment
   from CTE5 c5
   inner join #ParkingStatus ps on c5.ParkingStatusid=ps.ParkingStatus
    order by ReportDateTime desc

清理

drop table #Parking,#ParkingStatus,#Number,#ParkingTemp

替代和改进:

;WITH CTE
     AS (SELECT ParkingID,
                ParkingDateTime,
                COUNT(*) + 1 SplitCount,
                ParkingStatus AS InitialStatus
         FROM #ParkingTemp
         GROUP BY ParkingID,
                  ParkingDateTime,
                  ParkingStatus),
     DistinctIDCTE
     AS (SELECT DISTINCT
                ParkingID
         FROM #ParkingTemp),
     CTE1
     AS (SELECT Dates,
                DATEADD(hour, hrs, Dates) ReportDateTime,
                ParkingID
         FROM [CalendarDate],
              #Number N,
              DistinctIDCTE
         WHERE dates >= @From
               AND Dates <= @To),
     CTE2
     AS (SELECT c.ParkingID,
                DATEADD(minute, -DATEPART(minute, ParkingDateTime), ParkingDateTime) ParkingDate,
                ParkingDateTime,
                hrs AS rownum,
                InitialStatus
         FROM CTE C
              CROSS APPLY
         (
             SELECT hrs
             FROM #Number N
             WHERE c.SplitCount > n.Hrs
         ) ca),
     CTE5
     AS (SELECT c4.ParkingID,
                c4.ReportDateTime,
                CASE
                    WHEN rownum = 0
                         AND InitialStatus = 1
                    THEN 2
                    WHEN rownum = 1
                         AND InitialStatus = 1
                    THEN 3
                    WHEN rownum = 0
                         AND InitialStatus = 0
                    THEN 4
                    WHEN rownum = 1
                         AND InitialStatus = 0
                    THEN 3
                    ELSE 2
                END AS ParkingStatusid,
                CASE
                    WHEN rownum = 0
                    THEN DATEDIFF(minute, ReportDateTime, ParkingDateTime)
                    WHEN rownum = 1
                    THEN 60 - DATEPART(minute, ParkingDateTime)
                    ELSE 1.00
                END Duration,
                ParkingDateTime,
                rownum,
                InitialStatus
         FROM CTE1 c4
              LEFT JOIN CTE2 c2 ON c4.ParkingID = c2.ParkingID
                                   AND c2.ParkingDate = c4.ReportDateTime)
     SELECT c5.ParkingID,
            c5.ReportDateTime,
            c5.ParkingStatusid,
            Duration,
            PS.StatusName AS Comment
     FROM CTE5 c5
          INNER JOIN #ParkingStatus ps ON c5.ParkingStatusid = ps.ParkingStatus
     ORDER BY ReportDateTime DESC;

注意:清除我的疑虑。抛出不同的样本数据,例如在一小时内有超过 2 个停车位相同的停车位。

【讨论】:

  • @denzel,检查替代和改进,注意
猜你喜欢
  • 2020-07-18
  • 2020-09-19
  • 1970-01-01
  • 1970-01-01
  • 2020-04-11
  • 1970-01-01
  • 1970-01-01
  • 2015-04-08
  • 1970-01-01
相关资源
最近更新 更多