简体   繁体   中英

Calculate the time in minutes that a value has been greater than x

I want to be able to calculate the total time in minutes that the temperature column has been over a certain temperature. For example, I want to know how long in minutes, the temperature has been above 16.

If a reading at 12:28 was 16 and a reading at 12:30 is 17 , we are saying that from 12:28 to 12:30 , the value was 17 .

Furthermore, if the first or only reading is above x (17), this will be two minutes because when the device is started it takes x minutes (2 minutes in this instance) before the first reading is taken.

  • The SerialNumber is the serial number of the device reading the temperature.
  • The CombinDateTime is the time the temperature reading was taken.
  • The Temperature is the temperature value.

  SerialNumber, CombinDateTime, Temperature
  1000649496, 2018-12-05 10:56:52,    16.6
  1000649496, 2018-12-05 10:58:52,    17.3
  1000649496, 2018-12-05 11:00:52,    16.8
  1000649496, 2018-12-05 11:02:52,    16.6
  1000649496, 2018-12-05 11:04:52,    16.4
  1000649496, 2018-12-05 11:06:52,    16.3
  1000649496, 2018-12-05 11:08:52,    16.3
  1000649496, 2018-12-05 11:10:52,    16.2
  1000649496, 2018-12-05 11:12:52,    16.2
  1000649496, 2018-12-05 11:14:52,    16.2
  1000649496, 2018-12-05 11:16:52,    16.2
  1000649496, 2018-12-05 11:18:52,    16.2
  1000649496, 2018-12-05 11:20:52,    16.1
  1000649496, 2018-12-05 11:22:52,    16.1
  1000649496, 2018-12-05 11:24:52,    16.1
  1000649496, 2018-12-05 11:26:52,    16
  1000649496, 2018-12-05 11:28:52,    16
  1000649496, 2018-12-05 11:30:52,    16
  1000649496, 2018-12-05 11:32:52,    16
  1000649496, 2018-12-05 11:34:52,    16.1
  1000649496, 2018-12-05 11:36:52,    16.1
  1000649496, 2018-12-05 11:38:52,    16.1
  1000649496, 2018-12-05 11:40:52,    16.1
  1000649496, 2018-12-05 11:42:52,    16.1
  1000649496, 2018-12-05 11:44:52,    16.1
  1000649496, 2018-12-05 11:46:52,    16.1
  1000649496, 2018-12-05 11:48:52,    16
  1000649496, 2018-12-05 11:50:52,    16
  1000649496, 2018-12-05 11:52:52,    16
  1000649496, 2018-12-05 11:54:52,    16
  1000649496, 2018-12-05 11:56:52,    16
  1000649496, 2018-12-05 11:58:52,    16
  1000649496, 2018-12-05 12:00:52,    16.1
  1000649496, 2018-12-05 12:02:52,    16.1
  1000649496, 2018-12-05 12:04:52,    16.1
  1000649496, 2018-12-05 12:06:52,    16.1
  1000649496, 2018-12-05 12:08:52,    16
  1000649496, 2018-12-05 12:10:52,    16
  1000649496, 2018-12-05 12:12:52,    16
  1000649496, 2018-12-05 12:14:52,    16
  1000649496, 2018-12-05 12:16:52,    16
  1000649496, 2018-12-05 12:18:52,    16
  1000649496, 2018-12-05 12:20:52,    16
  1000649496, 2018-12-05 12:22:52,    16
  1000649496, 2018-12-05 12:24:52,    16
  1000649496, 2018-12-05 12:26:52,    16
  1000649496, 2018-12-05 12:28:52,    16
  1000649496, 2018-12-05 12:30:52,    16
  1000649496, 2018-12-08 08:08:52,    15.1
  1000649496, 2018-12-05 12:32:52,    16
  1000649496, 2018-12-05 12:34:52,    16
  1000649496, 2018-12-05 12:36:52,    16
  1000649496, 2018-12-05 12:38:52,    16

My query so far is very basic:

    SELECT SerialNumber, CombineDateTime, Temperature 
    FROM RawData
    WHERE Temperature > 16

The principal I have in mind is that I select the data-set and order by date and move through each row until I find a value that is over 16 . I then take the date and then move through the records until I find a value that is <= 16 , then take that date and time and datediff() the period in minutes .

I know you are not supposed to loop through SQL records, so I am thinking of using a CTE , but I am not too sure how to do this.

My expected results would be for example:

    SerialNumber, MinutesOver 
    1000649496, 1186

TIA

This looks like a gaps and islands problem (consecutive > 16 temperatures and <= 16 temperatures need to be grouped together) and one solution is as follows:

DECLARE @threshold DECIMAL(18, 2) = 16;
WITH cte1 AS (
    SELECT *, CASE 
           -- first row itself is greater than threshold
           WHEN Temperature  >  @threshold  AND  LAG(Temperature)  OVER (PARTITION BY SerialNumber ORDER BY CombinDateTime) IS NULL      THEN 1
           -- next row is greater than threshold
           WHEN Temperature <=  @threshold  AND LEAD(Temperature)  OVER (PARTITION BY SerialNumber ORDER BY CombinDateTime) > @threshold THEN 1
           -- prev row is greater than threshold
           WHEN Temperature <=  @threshold  AND  LAG(Temperature)  OVER (PARTITION BY SerialNumber ORDER BY CombinDateTime) > @threshold THEN 1
    END AS chg
    FROM @t
), cte2 AS (
    SELECT *, SUM(chg) OVER (PARTITION BY SerialNumber ORDER BY CombinDateTime) AS grp
    FROM cte1
)
SELECT SerialNumber
     , MIN(CombinDateTime) AS StartDateTime
     , MAX(CombinDateTime) AS EndDateTime
     , DATEDIFF(SECOND, MIN(CombinDateTime), MAX(CombinDateTime)) / 60.0 AS Total
FROM cte2
GROUP BY SerialNumber, grp
HAVING MAX(Temperature) > @threshold

Result:

SerialNumber  StartDateTime        EndDateTime          Total
1000649496    2018-12-05 10:56:52  2018-12-05 11:24:52  28.000000
1000649496    2018-12-05 11:32:52  2018-12-05 11:46:52  14.000000
1000649496    2018-12-05 11:58:52  2018-12-05 12:06:52  8.000000

A solution with LAG and rolling SUM window functions:

DECLARE @ThresholdTemperature DECIMAL(3, 1) = 16

;WITH BreakMarker AS
(
    -- Determine if the temperature is above or below the threshold
    SELECT
        M.*,
        LimitMarker = CASE WHEN M.Temperature > @ThresholdTemperature THEN 0 ELSE 1 END
    FROM
        #Measures AS M
),
LaggedChange AS
(
    -- Determine at which point in time the temperature moves between the threshold
    SELECT
        B.*,
        TempChange = CASE WHEN B.LimitMarker = LAG(B.LimitMarker, 1, 0) OVER (
            PARTITION BY 
                B.SerialNumber 
            ORDER BY 
                B.CombinDateTime ASC) THEN 0 ELSE 1 END
    FROM
        BreakMarker AS B
),
BreakGroups AS
(
    -- Generate a group ID value to calculate MAX and MIN
    SELECT
        L.*,
        BreakGroup = SUM(TempChange) OVER (PARTITION BY L.SerialNumber ORDER BY L.CombinDateTime ASC)
    FROM
        LaggedChange AS L
)
SELECT
    B.SerialNumber,
    MinCombinDateTime = MIN(B.CombinDateTime),
    MaxCombinDateTime = MAX(B.CombinDateTime),
    MinutesOver = DATEDIFF(MINUTE, MIN(B.CombinDateTime), MAX(B.CombinDateTime))
FROM
    BreakGroups AS B
GROUP BY
    B.SerialNumber,
    B.BreakGroup
HAVING
    MIN(B.Temperature) > @ThresholdTemperature

Result:

SerialNumber    MinCombinDateTime           MaxCombinDateTime           MinutesOver
1000649496      2018-12-05 10:56:52.000     2018-12-05 11:24:52.000     28
1000649496      2018-12-05 11:34:52.000     2018-12-05 11:46:52.000     12
1000649496      2018-12-05 12:00:52.000     2018-12-05 12:06:52.000     6

You can check the temporary results from the CTE here, so it's easier to understand the step by step logic:

SerialNumber    CombinDateTime              Temperature LimitMarker TempChange  BreakGroup
1000649496      2018-12-05 10:56:52.000     16.6        0           0           0
1000649496      2018-12-05 10:58:52.000     17.3        0           0           0
1000649496      2018-12-05 11:00:52.000     16.8        0           0           0
1000649496      2018-12-05 11:02:52.000     16.6        0           0           0
1000649496      2018-12-05 11:04:52.000     16.4        0           0           0
1000649496      2018-12-05 11:06:52.000     16.3        0           0           0
1000649496      2018-12-05 11:08:52.000     16.3        0           0           0
1000649496      2018-12-05 11:10:52.000     16.2        0           0           0
1000649496      2018-12-05 11:12:52.000     16.2        0           0           0
1000649496      2018-12-05 11:14:52.000     16.2        0           0           0
1000649496      2018-12-05 11:16:52.000     16.2        0           0           0
1000649496      2018-12-05 11:18:52.000     16.2        0           0           0
1000649496      2018-12-05 11:20:52.000     16.1        0           0           0
1000649496      2018-12-05 11:22:52.000     16.1        0           0           0
1000649496      2018-12-05 11:24:52.000     16.1        0           0           0
1000649496      2018-12-05 11:26:52.000     16.0        1           1           1
1000649496      2018-12-05 11:28:52.000     16.0        1           0           1
1000649496      2018-12-05 11:30:52.000     16.0        1           0           1
1000649496      2018-12-05 11:32:52.000     16.0        1           0           1
1000649496      2018-12-05 11:34:52.000     16.1        0           1           2
1000649496      2018-12-05 11:36:52.000     16.1        0           0           2
1000649496      2018-12-05 11:38:52.000     16.1        0           0           2
1000649496      2018-12-05 11:40:52.000     16.1        0           0           2
1000649496      2018-12-05 11:42:52.000     16.1        0           0           2
1000649496      2018-12-05 11:44:52.000     16.1        0           0           2
1000649496      2018-12-05 11:46:52.000     16.1        0           0           2
1000649496      2018-12-05 11:48:52.000     16.0        1           1           3
1000649496      2018-12-05 11:50:52.000     16.0        1           0           3
1000649496      2018-12-05 11:52:52.000     16.0        1           0           3
1000649496      2018-12-05 11:54:52.000     16.0        1           0           3
1000649496      2018-12-05 11:56:52.000     16.0        1           0           3
1000649496      2018-12-05 11:58:52.000     16.0        1           0           3
1000649496      2018-12-05 12:00:52.000     16.1        0           1           4
1000649496      2018-12-05 12:02:52.000     16.1        0           0           4
1000649496      2018-12-05 12:04:52.000     16.1        0           0           4
1000649496      2018-12-05 12:06:52.000     16.1        0           0           4
1000649496      2018-12-05 12:08:52.000     16.0        1           1           5
1000649496      2018-12-05 12:10:52.000     16.0        1           0           5
1000649496      2018-12-05 12:12:52.000     16.0        1           0           5
1000649496      2018-12-05 12:14:52.000     16.0        1           0           5
1000649496      2018-12-05 12:16:52.000     16.0        1           0           5
1000649496      2018-12-05 12:18:52.000     16.0        1           0           5
1000649496      2018-12-05 12:20:52.000     16.0        1           0           5
1000649496      2018-12-05 12:22:52.000     16.0        1           0           5
1000649496      2018-12-05 12:24:52.000     16.0        1           0           5
1000649496      2018-12-05 12:26:52.000     16.0        1           0           5
1000649496      2018-12-05 12:28:52.000     16.0        1           0           5
1000649496      2018-12-05 12:30:52.000     16.0        1           0           5
1000649496      2018-12-05 12:32:52.000     16.0        1           0           5
1000649496      2018-12-05 12:34:52.000     16.0        1           0           5
1000649496      2018-12-05 12:36:52.000     16.0        1           0           5
1000649496      2018-12-05 12:38:52.000     16.0        1           0           5
1000649496      2018-12-08 08:08:52.000     15.1        1           0           5

您正在寻找SUM日期分钟部分,然后按序列号分组

SELECT SUM(DATEPART(minute, [CombinDateTime])) AS total_call_time , [SerialNumber] FROM [dbo].[Table_1] WHERE [Temperature]>16 GROUP BY [SerialNumber];

You need to assign a group to each row. This group can be assigned as the number of values that exceed each row on or after each row. This will include the "closing" row in the group.

So the groups are assigned as:

SELECT rd.*,
       SUM(CASE WHEN Temperature <= 16 THEN 1 ELSE 0 END) OVER (PARTITION BY SerialNumber ORDER BY CombineDateTime DESC) as grp
FROM RawData rd;

Then you can use aggregation and filtering. So, this returns the time spans you want:

SELECT SerialNumber,
       MIN(CombineDateTime), MAX(CombineDateTime)
FROM (SELECT rd.*,
             SUM(CASE WHEN Temperature <= 16 THEN 1 ELSE 0 END) OVER (PARTITION BY SerialNumber ORDER BY CombineDateTime DESC) as grp
      FROM RawData rd
     ) rd
WHERE Temperature > 16
GROUP BY SerialNumber, grp;

And finally, you can calculate the total minutes:

SELECT SUM(DATEDIFF(minute, min_cdt, max_cdt)
FROM (SELECT SerialNumber,
             MIN(CombineDateTime) as min_cdt,
             MAX(CombineDateTime) as max_cdt
      FROM (SELECT rd.*,
                   SUM(CASE WHEN Temperature <= 16 THEN 1 ELSE 0 END) OVER (PARTITION BY SerialNumber ORDER BY CombineDateTime DESC) as grp
            FROM RawData rd
           ) rd
      WHERE Temperature > 16
      GROUP BY SerialNumber, grp
     ) s;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM