#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
.