基于模式SQL Server更新序列

时间:2017-05-24 10:50:11

标签: sql-server sql-server-2008 while-loop

我无法为问题引用正确的图块。

以下是我的表格。

enter image description here

预期输出:使用ID和SequenceNo列。无法上传图片。

ID  Act SequenceNo1
1   1   1
2   2   NULL
3   3   NULL
4   4   NULL
5   1   5
6   2   NULL
7   3   NULL
8   4   NULL
9   5   NULL
10  6   NULL
11  5   11
12  6   NULL
13  1   13
14  2   NULL
15  3   NULL
16  4   NULL
17  5   NULL
18  6   NULL
19  1   19
20  2   NULL
21  3   NULL
22  4   NULL
23  5   NULL
24  6   NULL

在开始的最后一列SequenceNo为NULL。

我的要求是,每当启动ID列的新系列时,都会将SequenceNo列的值更新为Act列。

Act列的值为1到6.可能会出现Act中缺少1到6之间的任何数字。

示例1:ID 1到4 - Act是正确的,但在下一行(ID = 5)Act重新启动。因此需要更新SequenceNo列。

示例2:ID 5到10是正确的。但是下一行(ID = 11; Act = 5)有新序列,因此需要更新SequenceNo列。

CREATE TABLE #tmp 
(
    ID int 
    , ScheduleID varchar(50)
    ,DCNumber VARCHAR(50)
    , BuildingID varchar(10)
    , StoreNumber int
    , [DayOfWeek] int
    , [Tm] varchar(10)
    ,[Act] int
    , SequenceNo int

)

INSERT INTO #tmp SELECT 1,'WAS',9003,900301,254,1,'00:00',1,NULL
INSERT INTO #tmp SELECT 2,'WAS',9003,900301,254,1,'00:00',2,NULL
INSERT INTO #tmp SELECT 3,'WAS',9003,900301,254,1,'00:00',3,NULL
INSERT INTO #tmp SELECT 4,'WAS',9003,900301,254,1,'00:00',4,NULL
INSERT INTO #tmp SELECT 5,'WAS',9003,900301,254,2,'00:00',1,NULL
INSERT INTO #tmp SELECT 6,'WAS',9003,900301,254,2,'00:00',2,NULL
INSERT INTO #tmp SELECT 7,'WAS',9003,900301,254,2,'00:00',3,NULL
INSERT INTO #tmp SELECT 8,'WAS',9003,900301,254,2,'00:00',4,NULL
INSERT INTO #tmp SELECT 9,'WAS',9003,900301,254,2,'00:00',5,NULL
INSERT INTO #tmp SELECT 10,'WAS',9003,900301,254,2,'00:00',6,NULL
INSERT INTO #tmp SELECT 11,'WAS',9003,900301,254,3,'00:00',5,NULL
INSERT INTO #tmp SELECT 12,'WAS',9003,900301,254,3,'00:00',6,NULL
INSERT INTO #tmp SELECT 13,'WAS',9003,900301,254,4,'00:00',1,NULL
INSERT INTO #tmp SELECT 14,'WAS',9003,900301,254,4,'00:00',2,NULL
INSERT INTO #tmp SELECT 15,'WAS',9003,900301,254,4,'00:00',3,NULL
INSERT INTO #tmp SELECT 16,'WAS',9003,900301,254,4,'00:00',4,NULL
INSERT INTO #tmp SELECT 17,'WAS',9003,900301,254,5,'00:00',5,NULL
INSERT INTO #tmp SELECT 18,'WAS',9003,900301,254,5,'00:00',6,NULL
INSERT INTO #tmp SELECT 19,'WAS',9003,900301,254,6,'00:00',1,NULL
INSERT INTO #tmp SELECT 20,'WAS',9003,900301,254,6,'00:00',2,NULL
INSERT INTO #tmp SELECT 21,'WAS',9003,900301,254,6,'00:00',3,NULL
INSERT INTO #tmp SELECT 22,'WAS',9003,900301,254,6,'00:00',4,NULL
INSERT INTO #tmp SELECT 23,'WAS',9003,900301,254,7,'00:00',5,NULL
INSERT INTO #tmp SELECT 24,'WAS',9003,900301,254,7,'00:00',6,NULL

我已经构建了一个逻辑,但这很耗时。

DECLARE  @Act INT, @iStart INT, @iMax INT, @iFirst INT
    SET @iFirst = 7
    SET @iMax = (SELECT MAX(ID) FROM #tmp)
    SET @iStart = 1
    WHILE(@iStart <= @iMax)
    BEGIN   
        SET @Act = (SELECT Act FROM #tmp WHERE ID = @iStart) 
        IF(@iFirst > @Act )
        BEGIN
            UPDATE #tmp SET SequenceNo = @iStart WHERE ID = @iStart     
        END
        SET @iFirst = @Act
        SET @iStart = @iStart + 1
    END

我正在寻找任何其他优化解决方案。

4 个答案:

答案 0 :(得分:0)

这是你想要的吗?

update tupd
set
    SequenceNo = tupd.ID
from (
    select
    *
        , LAG(t.Act, 1, null) over(order by t.ID) as LagAct
    from #tmp t
) tt
inner join #tmp tupd on tt.ID = tupd.ID
where tt.Act < tt.LagAct


select
*
from #tmp tt
order by tt.ID

答案 1 :(得分:0)

;WITH CTE 
AS 
(
    SELECT  ID,Act,RANK() OVER (PARTITION BY [DayOfWeek] ORDER BY Act) RN
    FROM    #tmp
)
UPDATE t
SET t.SequenceNo = t.id
FROM #tmp t
inner join CTE C
    ON t.ID = C.ID
WHERE C.RN = 1

答案 2 :(得分:0)

如果将当前行与之前的行进行比较,则可以在Act序列中找到中断。在当前的SQL Server版本中,即2012+,您可以使用LAG()方法执行此操作。在以前的版本中,您必须对当前ID值及其先前值执行自联接。如果当前和先前Act值之间的差值不是1,则序列中断。这仅适用于ID中没有间隙的情况。

此查询将在Act重新启动的每一行的SeqBreak列中返回1

select 
    t1.ID,
    t1.SequenceNo ,
    case when t1.act = t2.act+1 then '0' else '1' end as SeqBreak
from #tmp t1 left join #tmp t2 on t1.id=t2.id+1

您可以在CTE中使用它来仅选择Act中断的行。您可以直接更新CTE,至少在SQL Server 2014中。

with x as (
    select 
        t1.ID,
        t1.SequenceNo ,
        case when t1.act = t2.act+1 then '0' else '1' end as SeqBreak
    from #tmp t1 left join #tmp t2 on t1.id=t2.id+1 )
update x
set SequenceNo=x.ID
where seqbreak=1 and id>1

如果这不适用于SQL Server 2008,您必须在cte和表之间加入:

with x as (
    select 
        t1.ID,
        t1.SequenceNo ,
        case when t1.act = t2.act+1 then '0' else '1' end as SeqBreak
    from #tmp t1 left join #tmp t2 on t1.id=t2.id+1 )
update #tmp
set SequenceNo=x.ID
from #tmp inner join x on x.ID=#tmp.ID
where seqbreak=1 and #tmp.id>1

使用LAG()的等效查询,SQL Server 2012中的一个窗口函数更简单,速度提高了一倍,因为它避免了自联接:

with x as (
    select 
        ID,
        SequenceNo ,
        case when act = 1 +LAG(act,1) OVER (ORDER BY ID)  then '0' else '1' end as SeqBreak
    from #tmp)
update x
set SequenceNo=x.ID
where seqbreak=1 and id>1

答案 3 :(得分:0)

如果我在下面正确地提出您的问题,则查询应该获取所需的结果。

             update t3 set sequenceno = t3.id 
from
            #temp t1
            cross apply (select id+1 as idsum 
        from #temp 
            tmp where t1.id = tmp.id)t2
            inner join #temp t3
            on t3.id = t2.idsum
where t1.act>t3.act