2015-12-22 4 views
11

У меня есть таблица, которая выглядит следующим образом:SQL Server: условный агрегат;

Year  Value 
    ----------------- 
    2013  -0.0016 
    2014  -0.0001 
    2015  0.0025 
    2016  -0.0003 
    2017  0.0023 
    2018  0.0002 

И мне нужно, чтобы выполнить условный агрегат, который приводит к новой колонке. Условия заключаются в следующем:

Если значение отрицательное, агрегация начинается и не прекращается до тех пор, пока значение не будет положительным. Тогда ничего, пока значение не отрицательно снова ... Результат будет выглядеть следующим образом:

Year  Value  AggCol 
    2013  -0.0016  -0.0016 
    2014  -0.0001  -0.0017 
    2015  0.0025  0.0008 
    2016  -0.0003  -0.0003 
    2017  0.0023  0.002 
    2018  0.0002  0.0002 

Это UDF так близко, как я получил:

create function dbo.fn(@cYear numeric, @rate float) 
returns float 
as 
begin 
    declare @pYear numeric 
    declare @return float 

    set @pYear = @cYear - 1 

    set @return = (select 
         case 
          when Value < 0 and @rate > 0 then null 
          when Value < 0 then Value + @rate 
          else @rate 
         end 
        from Table1 
        where [year] = @pYear) 

    return @return 
end 

Я нормально с ответом в C# если это будет проще, но лучше SQL. Проблема с созданной мной функцией заключается в том, что мне нужно ухватить результаты из предыдущей строки, чтобы добавить к значению, когда значение положительное.

Я всю ночь здесь в поисках улик и нет радости ...

EDIT: Так что думайте о них как значения индекса потребительских цен за год, которые должны применяться на ваш счет мобильного телефона на ваш носитель ... Они будут только увеличивать ваш счет по индексу потребительских цен и никогда не уменьшать его (если индекс потребительских цен отрицателен) ... но они будут компенсировать отрицательный ИПЦ предыдущих лет по текущему индексу потребительских цен, если индекс потребительских цен в текущем году является положительным (или сумма результатов в положительном) ...

Это может или не может помочь, но это ситуация LOL.

+0

Какую версию SQL Server вы используете? –

+0

Вы можете сделать это, используя запрос в SQL Server, но для этого требуются рекурсивные CTE. –

+0

2012, и я в порядке с CTE ... имею доступ к dbo, поэтому могу делать все, что нужно. – user3486773

ответ

6
DECLARE @t TABLE ([Year] INT, Value MONEY) 

INSERT INTO @t 
VALUES (2013, -0.0016), 
     (2014, -0.0001), 
     (2015, 0.0025), 
     (2016, -0.0003), 
     (2017, 0.0023), 
     (2018, 0.0002) 

SELECT t1.Year , 
     t1.Value , 
     oa.AggCol 
FROM @t t1 
     OUTER APPLY (SELECT SUM(Value) AS AggCol 
         FROM  @t t2 
         WHERE  Year <= t1.Year 
           AND Year > (SELECT ISNULL(MAX(Year), 0) 
              FROM @t 
              WHERE Year < t1.Year AND Value > 0) 
        ) oa 

Выход:

Year Value AggCol 
2013 -0.0016 -0.0016 
2014 -0.0001 -0.0017 
2015 0.0025 0.0008 
2016 -0.0003 -0.0003 
2017 0.0023 0.002 
2018 0.0002 0.0002 

Это означает, что: для каждой строки дает мне сумму значений меньше или равно, чем текущая строка и больше, чем максимальный ряд с положительным значением, которое появляется перед текущей строкой или запуском от 0, если такого не найдено.

+0

Вы, сэр, урод природы! Я имею в виду, что в максимально возможной степени, как я взорван скоростью вы ответили на это! Большое спасибо!!!! – user3486773

+0

@ пользователь3486773, спасибо тоже за интересный вопрос. –

+0

Это отличная попытка и работает с данными теста, но она ломается, когда она принимает несколько положительных значений, чтобы сделать текущую сумму положительным числом. например изменение 2014 до 0,0001 и 2015 год будет иметь неправильное значение. – Matt

0

Вы также можете сделать это с помощью окна функции:

;WITH PrevValues AS (
    SELECT Year, Value, 
      LAG(Value) OVER (ORDER BY Year) AS prevValue 
    FROM Table1 
), Flags AS (
    SELECT Year, Value, 
     CASE 
      WHEN Value < 0 AND prevValue > 0 THEN 2 -- next slice 
      WHEN Value < 0 OR prevValue < 0 THEN 1 -- same slice 
      WHEN Value > 0 AND prevValue > 0 THEN -1 -- not in a slice 
     END AS flag 
    FROM PrevValues 
), Islands AS (
    SELECT Year, Value,  
     CASE 
      WHEN flag = -1 THEN -1 
      ELSE SUM(flag) OVER (ORDER BY Year)  
       - 
       ROW_NUMBER() OVER (ORDER BY Year) 
     END AS grp 
    FROM Flags 
) 
SELECT Year, Value, 
     CASE 
      WHEN grp = -1 THEN Value 
      ELSE SUM(Value) OVER (PARTITION BY grp ORDER BY Year) 
     END AS AggCol 
FROM Islands 
ORDER BY Year 

Идея состоит в том, чтобы определить, островки строк, над которым применяется текущая сумма.

Demo here

+0

Это отличная попытка и работает с данными теста, но она ломается, когда она принимает несколько положительных значений, чтобы сделать текущую сумму положительным числом. например изменение 2014 до 0,0001 и 2015 год будет иметь неправильное значение. – Matt

0
DECLARE @t TABLE ([Year] INT, Value MONEY) 
INSERT INTO @t 
VALUES (2013,-0.0016),(2014,0.0001),(2015,0.0025),(2016,-0.0003),(2017,0.0023),(2018,0.0002) 

;WITH cteRowNum AS (
    SELECT *, ROW_NUMBER() OVER (ORDER BY Year) as RowNum 
    FROM 
     @t 
) 
, cteRecursive AS (
    SELECT 
     Year 
     ,Value 
     ,Value as AggCol 
     ,RowNum 
    FROM 
     cteRowNum 
    WHERe 
     RowNum = 1 

    UNION ALL 

    SELECT 
     c.Year 
     ,c.Value 
     ,CASE 
      WHEN AggCol >= 0 THEN c.Value 
      ELSE AggCol + c.Value 
     END 
     ,c.RowNum 
    FROM 
     cteRecursive r 
     INNER JOIN cteRowNum c 
     ON r.RowNum + 1 = c.RowNum 
) 

SELECT Year, Value, AggCol 
FROM 
    cteRecursive 

Примечание Это другой набор данных, которую вы предоставили! вот результаты

Year Value  AggCol 
2013 -0.0016  -0.0016 
2014  0.0001  -0.0015 
2015  0.0025  0.001 
2016 -0.0003  -0.0003 
2017  0.0023  0.002 
2018  0.0002  0.0002 

Проблема с исходными тестовыми данными является то, что он не учитывает ситуации, когда он будет принимать несколько последовательных положительные записи, чтобы текущая сумма положительна. Впоследствии ОБА другие ответы в то время, когда я отправляю свой ответ, ошибочны. Поэтому я изменил только запись 2014 на положительный .0001, и вы можете увидеть, как это решение работает, а другие - нет.

Есть, вероятно, способы сделать это с оконными функциями, но рекурсивное CTE довольно прямо вперед, так что я пошел по этому пути:

  • Сначала построить row_number на наборе данных для использования в стыках для учета ситуации если в вашем наборе данных отсутствует что-либо.
  • Далее постройте рекурсивный cte и шаг 1 строку за раз, используя номер строки, и определите, нужно ли сбросить или добавить значение совокупности в зависимости от того, является ли предыдущее значение строк положительным или отрицательным.

Вот результаты Георгос & ответы Джорджи, если вы сделаете изменения в тестовых данных:

Year Value  AggCol 
2013 -0.0016  -0.0016 
2014  0.0001  -0.0015 
2015  0.0025  0.0025 
2016 -0.0003  -0.0003 
2017  0.0023  0.002 
2018  0.0002  0.0002 

Вы можете увидеть проблему с AggCol на год 2015 неправильно

Пожалуйста Заметьте, я думаю, что ответы - это большие попытки и показать некоторые реальные навыки/код, когда дело доходит до пробелов/островов. Я не пытаюсь атаковать, просто повышая качество сообщения.

Смежные вопросы