如何通过特定顺序检测值的更改

时间:2019-02-06 17:29:07

标签: sql sql-server tsql

给予

Id | RouteId | Milepoint | RoadCondition 
-: | :------ | --------: | :------------ 
 1 | A       |         0 | X             
 2 | A       |         1 | X             
 3 | A       |         4 | Y             
 4 | B       |         0 | Y             
 5 | B       |         2 | null 
 6 | B       |         5 | null 
 7 | B       |         6 | Z             
 8 | B       |        18 | Z             
 9 | C       |         0 | X             

我在追求

Id | RouteId | Milepoint | RoadCondition | ContinuousId
-: | :------ | --------: | :------------ | -----------:
 1 | A       |         0 | X             |            1
 2 | A       |         1 | X             |            1
 3 | A       |         4 | Y             |            2
 4 | B       |         0 | Y             |            3
 5 | B       |         2 | null          |            4
 6 | B       |         5 | null          |            5
 7 | B       |         6 | Z             |            6
 8 | B       |        18 | Z             |            6
 9 | C       |         0 | X             |            7
DECLARE @Example TABLE (
  Id INT primary key,
  RouteId varchar(50),
  Milepoint INT,
  RoadCondition varchar(50),
  ContinuousId INT
)

db <>提琴here

我正在使用SQL Server 2017。

我所追求的是生成ContinuousId的能力,它沿着RouteId ASC, Milepoint ASC行进,当RouteIdRoadCondition改变时,它会增加{{1} }

我知道我想使用ContinuousId,但是处理NULL是我不确定如何解决此问题的部分,因为我不能只是避免合并NULL,因为不能假定两个null设置为相同的值,则当RoadCondition的值为NULL时应递增

3 个答案:

答案 0 :(得分:2)

您可以将DENSE_RANK()COALESCE()结合使用:

select e.*,
       dense_rank() over (order by RouteId, coalesce(RoadCondition, cast(id as varchar(255)))) AS New_ContinuousId
from @Example e;

答案 1 :(得分:1)

不如DENSE_RANK聪明,但我认为这同样有效

DECLARE @Example TABLE (
    Id INT primary key,
    RouteId varchar(50),
    Milepoint INT,
    RoadCondition varchar(50)
)


INSERT INTO @Example
VALUES 
    (1, 'A', 0, 'X')
    ,(2, 'A', 1, 'X')
    ,(3, 'A', 4, 'Y')
    ,(4, 'B', 0, 'Y')
    ,(5, 'B', 2, NULL)
    ,(6, 'B', 5, NULL)
    ,(7, 'B', 6, 'Z')
    ,(8, 'B', 18, 'Z')
    ,(9, 'C', 0, 'X')

;WITH CTE AS(
    SELECT *
    ,CASE 
        WHEN                    
            RouteId = LAG(RouteId) OVER (PARTITION BY RouteId ORDER BY Milepoint) 
            AND
            RoadCondition = LAG(RoadCondition) OVER (PARTITION BY RouteId ORDER BY Milepoint) 
        THEN 0
        ELSE 1
    END AS StartOfNextIsland 
    FROM @Example
)

SELECT *, SUM(StartOfNextIsland) OVER (ORDER BY RouteId, Milepoint) AS ContinuousId
FROM CTE

答案 2 :(得分:0)

以下是CTE的

;with cte as (
    select *,1 [New_ContinuousId] from @Example e where Id=1
union all
    select e.*
        ,cte.New_ContinuousId+(case when 
                    e.RouteId is null
                    or e.RoadCondition is null
                    or cte.RoadCondition is null
                    or cte.RouteId  is null
                    or e.RouteId<>cte.RouteId  
                    or e.RoadCondition<>cte.RoadCondition  
                    then 1 else 0 end) [ccc] from @Example e
        inner join cte on cte.Id+1=e.Id
)
select * from cte

结果:

Id  RouteId Milepoint   RoadCondition   ContinuousId    New_ContinuousId
==  ======= =========   =============   ============    =================
1   A       0           X               1               1
2   A       1           X               1               1
3   A       4           Y               2               2
4   B       0           Y               3               3
5   B       2           NULL            4               4
6   B       5           NULL            5               5
7   B       6           Z               6               6
8   B       18          Z               6               6
9   C       0           X               7               7

下面是带有LAG的较短版本,其结果与上面相同。

;with cte as (
    select * ,case when LAG(RoadCondition+RouteId,1) over (order by Id)=RoadCondition+RouteId then null else 1 end  [cc]
    from @Example e 
)
select Id,RouteId,Milepoint,RoadCondition,ContinuousId,count(cc) over (order by Id) [New_ContinuousId] from cte