T-SQL Найти локальные максимумы - сумма максимума бегущего значения - PullRequest
0 голосов
/ 28 августа 2018

Есть таблица, в которой есть значения рабочего значения, которые иногда сбрасываются:

--------------------------------------
|         Time           |   Value   |
--------------------------------------
|2018-08-11 00:16:00.000 |     4     |
|2018-08-11 00:17:00.000 |     8     |
|2018-08-11 00:18:00.000 |     12    |
|2018-08-11 00:19:00.000 |     16    |
|2018-08-11 00:20:00.000 |     27    |
|2018-08-11 00:21:00.000 |     0     |   -- Doesn't have to be neccessary 0
|2018-08-11 00:22:00.000 |     3     |
|2018-08-11 00:23:00.000 |     5     |
|2018-08-11 00:24:00.000 |     4     |   -- Even going down, not passing the limit value
|2018-08-11 00:25:00.000 |     12    |
|2018-08-11 00:26:00.000 |     18    |
--------------------------------------

Я пытаюсь добиться общей суммы всех локальных максимумов. Он находит самый большой элемент перед сбросом - просто будет: 27, 5, 18.

НО: Существует также особый случай, когда локальный максимум должен игнорировать маленькие закрылки (потому что рабочее значение иногда может быть немного ниже). В приведенном выше примере следует игнорировать значение 5, поскольку следующим значением является 4, а затем оно продолжает расти. Фактический результат будет: 27, 18.


Результат: 27 + 18 = 45


Пример SQL

CREATE TABLE Data (
  [Time] [datetime] NOT NULL,
  [Value] [real] NOT NULL
);

INSERT INTO Data ([Time], [Value]) VALUES(CAST('2018-08-11 00:16:00' AS DATETIME),'4' );
INSERT INTO Data ([Time], [Value]) VALUES(CAST('2018-08-11 00:17:00' AS DATETIME),'8' );
INSERT INTO Data ([Time], [Value]) VALUES(CAST('2018-08-11 00:18:00' AS DATETIME),'12');
INSERT INTO Data ([Time], [Value]) VALUES(CAST('2018-08-11 00:19:00' AS DATETIME),'16');
INSERT INTO Data ([Time], [Value]) VALUES(CAST('2018-08-11 00:20:00' AS DATETIME),'27');
INSERT INTO Data ([Time], [Value]) VALUES(CAST('2018-08-11 00:21:00' AS DATETIME),'0' );
INSERT INTO Data ([Time], [Value]) VALUES(CAST('2018-08-11 00:22:00' AS DATETIME),'3' );
INSERT INTO Data ([Time], [Value]) VALUES(CAST('2018-08-11 00:23:00' AS DATETIME),'5' );
INSERT INTO Data ([Time], [Value]) VALUES(CAST('2018-08-11 00:24:00' AS DATETIME),'4' );
INSERT INTO Data ([Time], [Value]) VALUES(CAST('2018-08-11 00:25:00' AS DATETIME),'12');
INSERT INTO Data ([Time], [Value]) VALUES(CAST('2018-08-11 00:26:00' AS DATETIME),'18');

Предлагаемое решение / что я пробовал: Я думал о попытке найти локальный максимум на ROW_NUMBER() по столбцу Time и присоединении к той же таблице с +1 номером строки. Затем я могу сравнить 2 значения, и если разрыв будет слишком большим, я просто проигнорирую этот факт. Однако последняя запись там не выбрана. И я не слишком уверен насчет оптимизации / если предложенное решение будет работать так, как задумано.

WITH TAB0 AS (
    SELECT
        *, rn = ROW_NUMBER() OVER (ORDER BY Time)
    FROM
        Data
)
SELECT
    t1.Time,
    t1.Value as MT1,
    t2.Value as MT2
FROM
    TAB0 t1
    INNER JOIN TAB0 t2 ON t2.rn = t1.rn + 1
        AND (t2.Value + 1) < t1.Value                --put the limit here instead of "+1"
    ORDER BY t1.Time;

Ответы [ 2 ]

0 голосов
/ 28 августа 2018

После ответа Гордона он указал мне правильный путь, нам нужно работать со значениями previous и next. Что делает этот запрос:

select d.*,
    lag(value) over (order by time) as prev_value,
    lead(value) over (order by time) as next_value
from data d

Сравнивая предыдущее и следующее значения, мы можем отфильтровать максимум (и добавить туда ограничения):

select (value)     -- Gets all the values, if there is " SUM(Value) " - sum of all values will be get
from (select d.*,
         lag(value) over (order by time) as prev_value,
         lead(value) over (order by time) as next_value
  from data d
  ) d
where value > (prev_value +1) and value > (next_value +1);

/*   +1 in the last row sets the limit to 1      */

А теперь нам нужно обработать предельные случаи, что можно просто сделать, установив NULL в 0.


Полный запрос:

select SUM(value)
from (select d.*,
         ISNULL(lag(value) over (order by time), 0) as prev_value,
         ISNULL(lead(value) over (order by time), 0) as next_value
  from data d
) d
where value > (prev_value + 1) and value > (next_value + 1);
0 голосов
/ 28 августа 2018

Для локальных максимумов логика будет:

select sum(value)
from (select d.*,
             lag(value) over (order by time) as prev_value,
             lead(value) over (order by time) as next_value
      from d
     ) d
where value > prev_value and value > next_value;

Ваши расширенные условия в основном связаны с установлением других ограничений; это делает то, что вы описываете:

select sum(value)
from (select d.*,
             lag(value) over (order by time) as prev_value,
             lead(value) over (order by time) as next_value,
             lead(value, 2) over (order by time) as next_value2
      from d
     ) d
where value > prev_value and value > next_value and
      value > next_value2;
...