Как найти максимальное значение подряд с изменением - PullRequest
0 голосов
/ 13 июня 2019

У меня есть набор данных, который имеет 12 различных значений для идентификатора, а также начальные и конечные значения.То, что я хочу инициализировать, это взять начальное значение в качестве начального аргумента цикла и конечное значение в качестве последнего аргумента.Выполните поиск значений, соответствующих им, и найдите максимум из них.После нахождения максимального поиска по значениям снова запустите начальное и конечное значения и найдите самое длинное последовательное значение максимального значения.

Ниже я опубликовал пример набора данных:

    create table #sample_data(
    ID  VARCHAR(10), val1 INT, val2 INT, val3 INT, val4 INT, val5 INT, 
    val6 INT, val7 INT, val8 INT, val9 INT, val10 INT, val11 INT, val12 INT,
    startValue INT, endValue INT );

insert into #sample_data values
(1001,3,2,1,0,1,2,3,0,0,0,0,0,1,7),
(1002,1,2,3,4,0,0,0,1,2,3,0,0,1,12),
(1003,0,3,2,1,0,0,0,3,3,0,0,0,1,12),
(1004,0,1,2,4,4,0,0,0,0,0,0,0,3,9),
(1005,1,2,2,1,0,0,2,2,2,1,0,0,1,8);

Результат, который я ожидаю для Id =1001 начало = 1, конец = 7, максимальное значение равно 3, и это происходит 2 раза, но они не являются последовательными, поэтому конечный результат, который я хотел бы получить, равен 1.
Для идентификатора = 1002 начало = 1, конец = 12Максимальное значение равно 4, и это происходит только 1 раз, поэтому конечный результат должен быть равен 1.

Для ID = 1003 начало = 1, конец = 12, максимальное значение равно 3, 3 происходит три раза, но только 2 из нихследовательно, я ожидаю получить 2.
Для ID = 1004 start = 3, end = 9, max = 4, это происходит два раза подряд, поэтому выходной сигнал должен быть 2.

Для ID = 1005 start = 1, end = 8, max = 2, это в общей сложности 5 раз, 2 и 3 раза подряд, я ожидаю получить 3 в качестве конечного результата, так как он самый длинный.

1 Ответ

1 голос
/ 13 июня 2019

Если я правильно понял вопрос, результат для строки с Id 1005 должен быть 2, а не 3, потому что максимальное значение (которое равно 2) появляется последовательно в местах 2,3 и затем снова в местах 7,8, 9 - но endValue в этом ряду равно 8, и, следовательно, большее число последовательных не должно учитываться.

Исходя из этого понимания (которое может быть неверным, отсюда и комментарий, который я написал к вопросу), это можно сделать с помощью подхода, основанного на множестве (то есть без каких-либо циклов), с помощью некоторых приятных трюков SQL .

Итак, первое, что вы хотите сделать, это использовать cross apply с конструктором табличных значений для преобразования столбцов val1 ... val12 в строки. Я думаю, что это также можно сделать с помощью Pivot, но я так и не смог освоить Pivot, поэтому я предпочитаю другие решения, чтобы получить то же самое.
В моем коде этот шаг выполняется в первом общем табличном выражении (называемом CTEValues).

Далее вы используете трюк Ицик Бен-Гана для обработки пробелов и проблем островов , чтобы идентифицировать группы последовательных значений в каждой строке. Этот шаг сделан во втором cte (CTEGroups).

Третий и последний cte, называемый CTEConsecutive, использует простую группу by и count, чтобы получить количество последовательных максимальных значений в каждой строке исходной таблицы, при условии, что их столбцы расположены между startValue и EndValue.

Последнее, что нужно сделать, это получить максимальное значение этого числа для каждого идентификатора - и это должно дать вам желаемые результаты.

Вот полный код:

WITH CTEValues AS
(
    SELECT ID, startValue, EndValue, Val, ValId, IIF(Val = MAX(Val) OVER(PARTITION BY ID), 1, 0) As IsMax
    FROM #sample_data
    CROSS APPLY
    (
        SELECT *
        FROM (VALUES 
              (Val1, 1), 
              (Val2, 2), 
              (Val3, 3),
              (Val4, 4), 
              (Val5, 5), 
              (Val6, 6),
              (Val7, 7), 
              (Val8, 8), 
              (Val9, 9),
              (Val10, 10), 
              (Val11, 11), 
              (Val12, 12)
             )V(Val, ValId)
    ) vals
), CTEGroups AS
(
SELECT ID, startValue, EndValue, Val, ValId, IsMax,
        ROW_NUMBER() OVER(PARTITION BY ID ORDER BY ValId) - 
        ROW_NUMBER() OVER(PARTITION BY ID, IsMax ORDER BY ValId) As Grp
FROM CTEValues

), CTEConsecutive AS
(
    SELECT ID, COUNT(Val) As NumOfConsecutiveMaxValues --*,  OVER(PARTITION BY Id, Grp) As NumOfValues
    FROM CTEGroups
    WHERE IsMax = 1
    AND ValId >= startValue
    AND ValId <= EndValue
    GROUP BY ID, Grp
)

SELECT ID, MAX(NumOfConsecutiveMaxValues)
FROM CTEConsecutive
GROUP BY ID
ORDER BY Id

Вы можете увидеть живое демо на rextester.

Если, однако, я ошибаюсь в своем первоначальном предположении, а startvalue и endvalue относятся только к диапазону, в котором нужно искать максимальное значение (и это даст вам ожидаемые результаты, которые вы ' мы разместили в вопросе), вам понадобится еще один cte.

WITH CTEValues AS
(
    SELECT ID, startValue, EndValue, Val, ValId
    FROM #sample_data
    CROSS APPLY
    (
        SELECT *
        FROM (VALUES 
              (Val1, 1), 
              (Val2, 2), 
              (Val3, 3),
              (Val4, 4), 
              (Val5, 5), 
              (Val6, 6),
              (Val7, 7), 
              (Val8, 8), 
              (Val9, 9),
              (Val10, 10), 
              (Val11, 11), 
              (Val12, 12)
             )V(Val, ValId)
    ) vals
), CTEValuesWithMax AS
(
    SELECT ID, startValue, EndValue, Val, ValId,
            IIF(Val = (
                SELECT MAX(Val)
                FROM CTEValues AS T1
                WHERE T0.ID = T1.ID
                AND T1.ValId >= T1.startValue
                AND T1.ValId <= T1.EndValue
            ), 1, 0) As IsMax
    FROM CTEValues AS T0
)

Остальной код остается прежним, за исключением того, что CTEGroups теперь выбирается из CTEValuesWithMax вместо CTEValues.

Вы также можете увидеть живую демонстрацию этого.

...