Home > Net >  How can I get the information that the start time is the end time in the next change according to th
How can I get the information that the start time is the end time in the next change according to th

Time:01-03

There is a data set with continuous data coming from the table. I have a column with [Status] in it. I'm trying to calculate the time in between by taking the date from the row with [Status] = 0 and taking the time as "EndTime" from the first row with [Status] > 0 as "StartTime". But I couldn't do the query. My table is as below:

LINE TIMESTAMP CL_Name Status
2 2023-01-03 11:25:33.917 FILM 1
2 2023-01-03 11:20:33.917 FILM 1
2 2023-01-03 11:15:33.910 FILM 0
2 2023-01-03 11:10:33.903 FILM 0
2 2023-01-03 11:05:33.900 FILM 0

The table I'm trying to make is

LINE CL_Name StartTime EndTime TotalTime
2 FILM 2023-01-03 11:05:33.900 2023-01-03 11:25:33.917 20Min

Here, the algorithm is to find the date data as "[Status]" = 0, take the date data as "StartTime" and then find the first "[Status]" = 1, take the date data and take that data as "EndTime" and try to find the time difference between the two.

I wrote my sql query as follows, but I was not completely successful.

  SELECT [LINE], [CL_Name], StartTime, EndTime, 
      DATEDIFF(second, startTime, endTime) as TotalTime
 FROM ( select [LINE], [CL_Name], [TIMESTAMP], [Status],
    Min(case when [Status] > 0 then [TIMESTAMP] end) as EndTime,
    Min(case when [Status] = 0 then [TIMESTAMP] end) as StartTime
  from [dbo].[CL]
  group by [LINE], [CL_Name], [_VALUE], [TIMESTAMP], [Status]) as tt
  where StartTime IS NOT NULL and EndTime IS NOT NULL
  Order by [TIMESTAMP] desc
LINE CL_Name StartTime EndTime
2 FILM NULL 2023-01-03 11:25:33.917
2 FILM NULL 2023-01-03 11:20:33.917
2 FILM 2023-01-03 11:15:33.910 NULL
2 FILM 2023-01-03 11:10:33.903 NULL
2 FILM 2023-01-03 11:05:33.900 NULL

The output I wanted

LINE CL_Name StartTime EndTime TotalTime
2 FILM 2023-01-03 11:05:33.900 2023-01-03 11:25:33.917 20Min

CodePudding user response:

Try the following, explanation within query comments:

/* check if the status is changed over the increasing time,
   and set a value of 1 whenever a change is occur
*/
WITH Check_Changes AS
(
  SELECT *,
    IIF(Status <> LAG(Status, 1, Status) OVER (PARTITION BY LINE, CL_NAME ORDER BY TIMESTAMP), 1 , 0) flag
  FROM CL
),
/* create groups using a running sum of the calculated flag in
   the previous CTE. This will give the same number for the 
   consecutive similar status values i.e. for (0000,11,000,11) it 
   will be (0000,11,222,33)
*/
Craete_Groups AS
(
  SELECT *, 
    SUM(flag) OVER (PARTITION BY LINE, CL_NAME ORDER BY TIMESTAMP)  
    CASE WHEN SUM(flag) OVER (PARTITION BY LINE, CL_NAME ORDER BY TIMESTAMP) % 2 = Status
    THEN 0 ELSE 1 END AS grp
FROM Check_Changes
/*  CASE WHEN SUM(flag) OVER ... is to check if the status begins with a 0 value. 
    In the typical case, the even groups will be within status=0 and the odd groups will be within status=1.
    This will add 1 to the group value if the status does not start with 0 i.e. for status = (1111,000,111)
    it will be (1111,222,333) instead of (0000,111,222)
*/
)
/* now you can connect every two consecutive groups together by using grp/2
i.e. groups (0000,11,222,33) will be (0000,00,111,11)
*/
SELECT LINE, CL_Name,
       MIN(CASE WHEN Status = 0 THEN TIMESTAMP END) StartTime, 
       MAX(CASE WHEN Status = 1 THEN TIMESTAMP END) EndTime, 
       DATEDIFF(MINUTE, 
                MIN(CASE WHEN Status = 0  THEN TIMESTAMP END), 
                MAX(CASE WHEN Status = 1  THEN TIMESTAMP END)
               ) TotalTime
FROM Craete_Groups
GROUP BY LINE, CL_Name, grp/2
ORDER BY LINE, CL_Name, MIN(TIMESTAMP)

See a demo on an extended data sample.

CodePudding user response:

On solution you can try is as follows, use the analytic function to partition and order your data based on the Timestamp.

You want the first row per Line and the first row per Line where the Status is 1.

Your desired results don't agree with your description however where you use the last row where Status = 1 - in which case change the Min below to Max.

with s as (
  select *, 
    Row_Number() over(partition by line, CL_Name, order by timestamp) rn,
    Min(case when status = 1 then timestamp end) over(partition by line, CL_Name) endtime
  from t
)
select 
  line, 
  CL_Name, 
  Timestamp 
  StartTime, 
  EndTime, 
  DateDiff(minute, Timestamp, EndTime) TotalTime
from s
where rn = 1;
  • Related