【问题标题】:Update sequence based on pattern SQL Server基于模式 SQL Server 的更新序列
【发布时间】:2017-10-24 16:04:03
【问题描述】:

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

下面是我的桌子。

预期输出:带有 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。`

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

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

我正在寻找任何替代的优化解决方案。

【问题讨论】:

  • MySQL 是一个不同的 rdbms 产品,它与 ms sql server 无关。请仅在您的问题中使用相关的产品标签。
  • 您使用的是什么版本的 sql server?另外,您能否编辑您的问题以将示例数据添加为 DDL+DML?
  • SQL Server 2012 具有排名和窗口函数,如 ROW_NUMBER、RANK、LEAD、LAG 等,这使得该查询变得微不足道。它也是最早支持的 SQL Server 版本。在以前的版本中,有有种方法可以检测没有循环的序列中的中断,尽管它们更难编写。也许您应该迁移到受支持的服务器版本?例如,在序列中找到一个中断只需要找到Act - LAG(Act,1) 不同于 1 的行
  • @PanagiotisKanavos - 谢谢。但我有 Microsoft SQL Server 2008 (SP3) - 10.0.5520.0 (X64) 版本。还有其他帮助吗?

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


【解决方案1】:

如果我在下面的查询中正确回答了您的问题,则应该获取所需的结果。

             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

【讨论】:

    【解决方案2】:
    ;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
    

    【讨论】:

    • 我希望将 ID 字段的值放入 SequenceNo 字段。你的查询对我没有帮助。
    • 你能举一个例子说明这个序列应该是什么样子吗?还更新了我的答案。现在检查它是否符合您的要求。
    • 添加了预期的输出。
    【解决方案3】:

    如果您将当前行与前一行进行比较,您可以在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
    

    【讨论】:

      【解决方案4】:

      这是你想要的吗?

      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
      

      【讨论】:

      • 我的版本是 Microsoft SQL Server 2008 (SP3) - 10.0.5520.0 (X64) 。我收到错误消息,因为“未启用并行数据仓库 (PDW) 功能。”
      • @AK47 正如我在 cmets 中提到的,这些不是 PDW 功能,这些是 SQL Server 2012+ 功能。您正在运行一个非常旧的(10 年)不受支持的版本。这使得写答案更难。您可以尝试在匹配列上进行自连接,以尝试在序列中查找前后行
      猜你喜欢
      • 2013-09-05
      • 2013-02-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-01-12
      • 1970-01-01
      • 1970-01-01
      • 2019-08-10
      相关资源
      最近更新 更多