SQL Server 2012:获取出现次数并使用动态计算持续时间 SQL

SQL Server 2012 : get number of occurrences and calculate duration with Dynamic SQL

我有一个 table 过程设定点和过程值 (PV)。

如下所示 table:

    Timestamp       Setpoint             PV 
    ---------       ---------       -------------
    t1                  100             125
    t2                  100              95  *
    t3                  100              98
    t4                  100              88
    t5                  100             105 
    t6                  100              59  *
    t7                  100              90
    t8                  100             101
    t9                  100              70  *
    t10                 100             101

我想要做的是创建一行作为结果的查询,以计算 PV 低于设定值并返回高于设定值的次数。并计算低于设定值的持续时间。

结果应该是这样的

    NumberofOccurance       Duration
    ------------------      ---------
            3               (t5-t2)+(t8-t6)+(t10-t9)

注意: 正确的出现次数是 3 在这种情况下不是 6 我认为最难的部分是持续时间。

有什么想法吗?

提前致谢

编辑:我在下面得到了几个很好的答案,但是,如果我有多个 PV(我实际上在不同的列中有 10 个不同的 PV,每个 PV 在两个不同的列中有两个设定点,这是20 列用于设定值。 全部合而为一 table。以 1 秒为间隔的时间戳。 知道如何对每个 PV 执行相同的查询吗? 我正在考虑 Dynamic SQL with Cursor 和下面的 CTE 选项全部组合在一起。但这真的很难。

您可以使用 lag 到 select 之前的 pv 和设置点值,以及仅 select 当前 pv 低于设置点但之前的 pv 不低于先前的设定值:

select *
from (
    select *,
        lag(Setpoint) over (order by Timestamp) previous_setpoint,
        lag(PV) over (order by Timestamp) previous_pv
    from Table1
) t where PV < Setpoint
and previous_pv >= previous_setpoint

要获得上面查询中 pv 的负值的持续时间总和,即 -t2 -t6 -t9。棘手的部分是正值。第一行什么都不加。对于除第一行和最后一行之外的所有行,仅添加 previous_pv。在最后一行添加 next_pvprevious_pv

Timestamp第1,2,3行对应t2 t6 t9。 prev_timestamp第2行对应t5 prev_timestamp 在第 3 行(最后一行)对应于 t8 next_timestamp 在第 3 行(最后一行)对应于 t10

select count(*), sum(case 
            when rn_asc = 1 then -Timestamp
            when rn_desc = 1 then -Timestamp + next_timestamp + prev_timestamp
            else -Timestamp + prev_timestamp
            end)
from (
    select *, 
     row_number() over (order by Timestamp) rn_asc,
     row_number() over (order by Timestamp desc) rn_desc
    from (
        select *,
            lag(Setpoint) over (order by Timestamp) previous_setpoint,
            lag(PV) over (order by Timestamp) previous_pv,
            lag(Timestamp) over (order by Timestamp) prev_timestamp,
            lead(Timestamp) over (order by Timestamp) next_timestamp
        from Table1
    ) t where PV < Setpoint
    and previous_pv >= previous_setpoint
) t

这是一个演示 http://sqlfiddle.com/#!6/b6ac5/5

这是一个典型的孤岛问题。解决这个问题的步骤,

  1. 查找范围开始和范围结束
  2. 将范围开始和范围结束合并为一行
  3. 按范围开始分组

这是代码

WITH T0 AS
(
    SELECT [Timestamp], CASE WHEN PV >= SetPoint THEN 1 ELSE 0 END AS pvType
    FROM table
),
T1 AS
(
    SELECT [Timestamp], 
        CASE 
            WHEN LAG(pvType) OVER(ORDER BY [Timestamp]) = 1 AND pvType = 0 THEN 1 
            ELSE 0 
        END AS pvStart,
        CASE 
            WHEN LAG(pvType) OVER(ORDER BY [Timestamp]) = 0 AND pvType = 1 THEN 1 
            ELSE 0 
        END AS pvEnd
    FROM T0
),
T2 AS 
(
    SELECT [Timestamp] AS timestampStart, 
        CASE
            WHEN pvEnd = 1 THEN [Timestamp] 
            ELSE LEAD([Timestamp]) OVER(ORDER BY [Timestamp]) 
        END AS timestampEnd, 
        pvStart
    FROM T1
    WHERE pvStart = 1 OR pvEnd = 1
)
SELECT 
    COUNT(*) AS Occurance, 
    -- Depending Timestamp type, you may want DATEDIFF
    SUM(timestampEnd - timestampStart) AS Duration
FROM T2
WHERE pvStart = 1

又长又丑但它有效

CREATE TABLE MyTable
  (
     TimeSt   INT,
     SetPoint INT,
     PV       INT
  )

INSERT INTO MyTable
VALUES      (1,             100,             122),
            (2,             100,             95),
            (3,             100,             98),
            (4,             100,             88),
            (5,             100,             105),
            (6,             100,             59),
            (7,             100,             90),
            (8,             100,             101),
            (9,             100,             70),
            (10,             100,             101);

WITH CTE
     AS (SELECT
           *
           ,lag(TimeSt, 1) OVER(ORDER BY TimeSt) AS LagTimeSt
           ,lag(SetPoint, 1) OVER(ORDER BY TimeSt) AS LagSetPoint
           ,lag(PV, 1) OVER(ORDER BY TimeSt) AS LagPV
         FROM
           MyTable),
     CTE2
     AS (SELECT
           *
           ,CASE
              WHEN (PV < SetPoint AND LagPV > LagSetPoint) 
                THEN 1 ELSE 0 END AS FirstDrop
         FROM
           CTE
         WHERE
          (PV < SetPoint AND LagPV > LagSetPoint)
           OR (PV > SetPoint AND LagPV < LagSetPoint)),
     CTE3
     AS (SELECT
           Lead(timest) OVER(ORDER BY TimeSt) UpTime
           ,*
         FROM
           CTE2)
SELECT
  sum(firstDrop)        AS Occur
  ,sum(uptime - Timest) AS DownTime
FROM
  CTE3 
Where FirstDrop = 1