根据SQL Server中的其他列值推断一列的值

时间:2019-04-19 10:15:52

标签: sql-server 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,所以这两个日期的状态也为真)

3 个答案:

答案 0 :(得分:0)

CASE语句将为您解决此问题。 OUTER APPLY将解决第二个要求。

在我看来,

APPLYCROSS APPLYOUTER 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)

为此,您真正想要的是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;

Here是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;

第一个版本更好地遵循了您所描述的逻辑。

我无法从问题中得知您是否要基于runningspeedstatuscode来构建逻辑。 runningspeed的逻辑非常相似。

答案 2 :(得分:0)

使用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 |