Вывод значения одного столбца на основе значений других столбцов в SQL Server

#sql-server #tsql

#sql-сервер #tsql

Вопрос:

Если скорость выполнения дня> 0, его статус true.

В случае отсутствия данных за определенный день выведите последнее записанное значение кода состояния (другого
столбца в таблице) и, если значение равно нулю, отметьте статус выполнения как true. Для кодов, отличных от нуля, отметьте статус как false.

Смотрите таблицу ниже

 Day       Run speed Statuscode  Status  
---------------------------------------
1-Jan-14    55         0        TRUE  
2-Jan-14    60         0        TRUE  
3-Jan-14    58         0        TRUE  
4-Jan-14    61         0        TRUE  
5-Jan-14    57         0        TRUE  
6-Jan-14    56         0        TRUE  
7-Jan-14    60         0        TRUE  
8-Jan-14                        TRUE  
9-Jan-14                        TRUE  
10-Jan-14   55         0        TRUE  
11-Jan-14   56         0        TRUE  
12-Jan-14   60         0        TRUE  
13-Jan-14    0        20        FALSE  
 

(пример 8-jan-14/9-jan-14 не имеет значения, но поскольку 7-е число было записано в последний раз, и оно было истинным, поэтому статус этих двух дат также является истинным)

Комментарии:

1. Вы опубликовали требования, но забыли включить свою работу и вопрос.

Ответ №1:

Использовать OUTER APPLY как более гибкую задержку

Живой тест: http://sqlfiddle.com /#!18/4c73f/18

 select 
    o.*,
    Status = 
        convert(bit,
            case 
                when o.RunningSpeed > 0 
                or   o.RunningSpeed is null and prev.StatusCode = 0 then 
                    1
                else 
                    0
            end)    
from tbl o
outer apply
(
    select top 1 StatusCode
    from tbl i
    where i.Day < o.Day and i.StatusCode is not null
    order by i.Day desc
) prev
 

Вывод:

 |                  Day | RunningSpeed | Statuscode | Status |
|----------------------|--------------|------------|--------|
| 2014-01-01T00:00:00Z |           55 |          0 |   true |
| 2014-01-02T00:00:00Z |           60 |          0 |   true |
| 2014-01-03T00:00:00Z |           58 |          0 |   true |
| 2014-01-04T00:00:00Z |           61 |          0 |   true |
| 2014-01-05T00:00:00Z |           57 |          0 |   true |
| 2014-01-06T00:00:00Z |           56 |          0 |   true |
| 2014-01-07T00:00:00Z |           60 |          0 |   true |
| 2014-01-08T00:00:00Z |       (null) |     (null) |   true |
| 2014-01-09T00:00:00Z |       (null) |     (null) |   true |
| 2014-01-10T00:00:00Z |           55 |          0 |   true |
| 2014-01-11T00:00:00Z |           56 |          0 |   true |
| 2014-01-12T00:00:00Z |           60 |          0 |   true |
| 2014-01-13T00:00:00Z |            0 |         20 |  false |
 

Ответ №2:

CASE Заявление решит эту проблему за вас. И OUTER APPLY решит второе требование.

APPLY , CROSS APPLY и OUTER APPLY , на мой взгляд, как наиболее понятные и недостаточно используемые итераторы. Но как только вы осознаете их силу и поймете варианты использования, они станут смертоносным оружием в вашем арсенале инструментов. Они особенно полезны в TOP(n) [by some logic] ситуациях, когда встроенные функции не соответствуют вашим требованиям.

Обратите внимание на использование BIT для представления логического TRUE/FALSE

 SELECT Day
     , RunningSpeed
     , PreviousRunningSpeed
     , CASE 
        WHEN RunningSpeed IS NULL AND PreviousRunningSpeed > 0 THEN 1
        WHEN RunningSpeed > 0 THEN 1
        ELSE 0
       END AS Status 
  FROM (SELECT Day
             , RunningSpeed  
             , d.RunningSpeed AS PreviousRunningSpeed            
          FROM tbl AS t1
               OUTER APPLY (SELECT TOP(1) 
                                   RunningSpeed
                              FROM tbl AS t2
                             WHERE RunningSpeed IS NOT NULL
                                   AND t2.Day < t1.Day
                             ORDER BY Day ASC)) AS d
 

Комментарии:

1. получить первую часть легко и похоже на то, что вы упомянули, если его> 0 мы можем указать как true . но где я сталкиваюсь с трудностями, так это во второй части, т.е. Если данных нет, мы должны вернуться и проверить, был ли последний записанный код равен 0

2. Эта часть также может быть очень простой. Какова ваша платформа? SQL Server?

3. Но используя задержку, которую вы указываете, чтобы вернуться только на один, установив смещение равным 1. но мы не знаем, всего ли это 1 день или больше

4. Вы можете OUTER APPLY , если ваша «предыдущая» логика более сложна, чем то, что LAG вы можете предложить.

Ответ №3:

Что вам действительно нужно для этого LAG(IGNORE NULLS) , так это то, что SQL Server этого не поддерживает.

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

 select t.*,
       (case when statuscode = 0 then 'TRUE'
             when statuscode <> 0 then 'FALSE'
             when (max(case when statuscode = 0 then day end) over (order by day) >
                   coalesce(max(case when statuscode <> 0 day date end) over (order by day), '2000-01-01')
                  )
             then 'TRUE'
             else 'FALSE'
        end) as status
from t;
 

Вот скрипка db<> .

На самом деле вы можете упростить это только до последних условий:

 select t.*,
       (case when (max(case when statuscode = 0 then day end) over (order by day) >
                   coalesce(max(case when statuscode <> 0 then day end) over (order by day), '2000-01-01')
                  )
             then 'TRUE'
             else 'FALSE'
        end) as status
from t;
 

Первая версия лучше соответствует логике, которую вы описываете.

Я не могу сказать из вопроса, хотите ли вы основывать логику на runningspeed or statuscode . Логика была бы очень похожа на runningspeed .