【问题标题】:If the difference between two sequences is bigger than 30, deduct bigger sequence如果两个序列之间的差值大于 30,则减去更大的序列
【发布时间】:2015-12-04 21:02:40
【问题描述】:

我很难尝试进行获取大量数字、数字序列的查询,如果其中两个之间的差大于 30,则序列将从该数字重置。所以,我有下表,除了第一列之外还有另一列,应该保持原样:

+----+--------+--------+
| Id | Number | Status |
+----+--------+--------+
|  1 |      1 | OK     |
|  2 |      1 | Failed |
|  3 |      2 | Failed |
|  4 |      3 | OK     |
|  5 |      4 | OK     |
|  6 |     36 | Failed |
|  7 |     39 | OK     |
|  8 |     47 | OK     |
|  9 |     80 | Failed |
| 10 |    110 | Failed |
| 11 |    111 | OK     |
| 12 |    150 | Failed |
| 13 |    165 | OK     |
+----+--------+--------+

它应该变成这个:

+----+--------+--------+
| Id | Number | Status |
+----+--------+--------+
|  1 |      1 | OK     |
|  2 |      1 | Failed |
|  3 |      2 | Failed |
|  4 |      3 | OK     |
|  5 |      4 | OK     |
|  6 |      1 | Failed |
|  7 |      4 | OK     |
|  8 |     12 | OK     |
|  9 |      1 | Failed |
| 10 |      1 | Failed |
| 11 |      2 | OK     |
| 12 |      1 | Failed |
| 13 |     16 | OK     |
+----+--------+--------+

感谢您的关注,如有任何疑问,我将竭诚为您解答! :)

编辑:此表的示例:http://sqlfiddle.com/#!6/ded5af

【问题讨论】:

  • 我想我理解您想要做什么,但您需要在此处再添加一列用于订购。这只是一个选择语句还是你试图更新这个表?有了您的结果,就没有什么可以维持秩序了。真正有帮助的是您可以发布 ddl 和示例数据。 sqlfiddle.com 将是一个很好的地方。
  • @SeanLange 事实上,我有一个排序列,我会在编辑时添加它。数据是机密且密集的,因此我针对我的问题对其进行了简化。
  • 了解数据敏感性。没有大碍。你用的是什么版本的sql server?您能否将其转换为 sqlfiddle,以便我们可以直接解决问题,而无需将您的示例转换为工作示例?
  • SQL 服务器 2014!我会努力做到的!谢谢:)
  • 在 CASE 语句中使用 LAG() 函数

标签: sql sql-server sql-server-2014


【解决方案1】:

有了这个测试用例:

declare @data table (id int identity, Number int, Status varchar(20));
insert @data(number, status) values
     ( 1,'OK')
    ,( 1,'Failed')
    ,( 2,'Failed')
    ,( 3,'OK')
    ,( 4,'OK')
    ,( 4,'OK')      -- to be deleted, ensures IDs are not sequential
    ,(36,'Failed')  -- to be deleted, ensures IDs are not sequential
    ,(36,'Failed')
    ,(39,'OK')
    ,(47,'OK')
    ,(80,'Failed')
,(110,'Failed')
,(111,'OK')
,(150,'Failed')
,(165,'OK')
;

delete @data where id between 6 and 7;

这个 SQL:

with renumbered as (
    select rn = row_number() over (order by id), data.*
    from @data data
),
paired as (
    select
        this.*,
        startNewGroup = case when this.number - prev.number >= 30 
                               or prev.id is null then 1 else 0 end
    from renumbered this
    left join renumbered prev on prev.rn = this.rn -1
),
groups as (
    select Id,Number, GroupNo = Number from paired where startNewGroup = 1
)
select
     Id
    ,Number = 1 + Number - (
                    select top 1 GroupNo 
                    from groups where groups.id <= paired.id 
                    order by GroupNo desc)
    ,status
from paired
;

满足要求:

Id          Number      status
----------- ----------- --------------------
1           1           OK
2           1           Failed
3           2           Failed
4           3           OK
5           4           OK
8           1           Failed
9           4           OK
10          12          OK
11          1           Failed
12          1           Failed
13          2           OK
14          1           Failed
15          16          OK

更新:使用新的 LAG() 函数允许在早期没有自连接的更简单的 SQL:

with renumbered as (
    select
         data.*
        ,gap = number - lag(number, 1) over (order by number)
    from @data data
),
paired as (
    select
        *,
        startNewGroup = case when gap >= 30 or gap is null then 1 else 0 end
    from renumbered 
),
groups as (
    select Id,Number, GroupNo = Number from paired where startNewGroup = 1
)
select
     Id
    ,Number = 1 + Number - ( select top 1 GroupNo 
                             from groups 
                             where groups.id <= paired.id 
                             order by GroupNo desc
                           )
    ,status
from paired
;

【讨论】:

  • 这不能产生所需的输出。在大于 30 的间隙之后,下一行不是下一个连续值。
  • @SeanLange:现在将&gt; 30 更改为&gt;= 30
  • 您的编辑 >= 30 现在可以正常工作了。 :) 如果我有时间,我会尝试将不使用递归 cte 的东西放在一起,但这肯定有效。干得好。
  • GroupNo 是干什么用的?与Number 相同。我删除了它,替换为Number 并得到了相同的结果。
  • @Andrew:很好;它是早期(更复杂)实现的僵尸。
【解决方案2】:

我不值得回答,但我认为这更短

with gapped as 
(   select id, number, gap = number - lag(number, 1) over (order by id)
    from @data data
),
select Id, status
       ReNumber = Number + 1 - isnull( (select top 1 gapped.Number 
                                          from gapped 
                                         where gapped.id <= data.id 
                                           and gap >= 30 
                                         order by gapped.id desc), 1)  
from @data data;

【讨论】:

  • 我正在使用这个版本,并真诚地感谢您和@Andrew 对简单性的关注。
【解决方案3】:

这只是 Pieter Geerkens 稍微简化的答案。我删除了一些中间结果和列:

with renumbered as (
    select data.*, gap = number - lag(number, 1) over (order by number)
    from @data data
),
paired as (
    select *
    from renumbered 
    where gap >= 30 or gap is null
)

select Id, Number = 1 + Number - (select top 1 Number 
                             from paired 
                             where paired.id <= renumbered.id 
                             order by Number desc)
    , status
from renumbered;

应该是评论,但是太长了,无法理解。

【讨论】:

    【解决方案4】:

    如果您的 ID 不是按顺序排列的,您可能需要在此之前创建另一个 cte 并使用 row_number 而不是 ID 来加入递归 cte

    WITH cte AS 
    (   SELECT 
            Id, [Number], [Status],
            0 AS Diff,
            [Number] AS [NewNumber]
        FROM
            Table1
        WHERE Id = 1
        UNION ALL 
        SELECT 
            t1.Id, t1.[Number], t1.[Status],
            CASE WHEN t1.[Number] - cte.[Number] >= 30 THEN t1.Number - 1 ELSE Diff END,
            CASE WHEN t1.[Number] - cte.[Number] >= 30 THEN 1 ELSE t1.[Number] - Diff END
        FROM Table1 t1
        JOIN cte ON cte.Id + 1 = t1.Id
    )
    SELECT Id, [NewNumber], [Status] 
    FROM cte
    

    SQL Fiddle

    这是另一个 SQL Fiddle 示例,说明如果 ID 不是连续的,您会做什么......

    SQL Fiddle 2

    如果 sql fiddle 停止工作

    --Order table to make sure there is a sequence to follow
    WITH OrderedSequence AS
    (
        SELECT 
            ROW_NUMBER() OVER (ORDER BY Id) RnId,
            Id,
            [Number],
            [Status]
        FROM 
            Sequence
    ),
    RecursiveCte AS 
    (   SELECT 
            Id, [Number], [Status],
            0 AS Diff,
            [Number] AS [NewNumber],
            RnId
        FROM
            OrderedSequence
        WHERE Id = 1
        UNION ALL 
        SELECT 
            t1.Id, t1.[Number], t1.[Status],
            CASE WHEN t1.[Number] - cte.[Number] >= 30 THEN t1.Number - 1 ELSE Diff END,
            CASE WHEN t1.[Number] - cte.[Number] >= 30 THEN 1 ELSE t1.[Number] - Diff END,
            t1.RnId      
        FROM OrderedSequence t1
        JOIN RecursiveCte cte ON cte.RnId + 1 = t1.RnId
    )
    SELECT Id, [NewNumber], [Status] 
    FROM RecursiveCte
    

    【讨论】:

    • 这不能产生所需的输出。在大于 30 的间隙之后,下一行不是下一个连续值。
    • 你确定吗?它符合他的预期结果吗? @SeanLange
    • 是的。小提琴有问题。您上面的链接现在一直在崩溃。
    • @SeanLange 链接对我来说没问题.. 试试第二个.. SQL fiddle 总是让我适应
    • 我知道你的意思。整洁的工具,但似乎确实有很多问题。
    【解决方案5】:

    我在这里尝试优化查询,因为处理我的数据需要 1 小时 20 分钟。经过进一步研究,我把它降到了 30 多岁。

    WITH AuxTable AS 
    (   SELECT
            id, 
            number,
            status,
    
            relevantId = CASE WHEN 
                number = 1 OR 
                ((number - LAG(number, 1) OVER (ORDER BY id)) > 29)
            THEN id
            ELSE NULL
            END,
    
            deduct = CASE WHEN 
                ((number - LAG(number, 1) OVER (ORDER BY id)) > 29)
            THEN number - 1
            ELSE 0
            END
    
        FROM @data data
    )
    ,AuxTable2 AS
    (
        SELECT 
            id, 
            number,
            status,
            AT.deduct, 
    
            MAX(AT.relevantId) OVER (ORDER BY AT.id ROWS UNBOUNDED PRECEDING ) AS lastRelevantId
    
        FROM AuxTable AT
    )
    
        SELECT 
            id, 
            number,
            status,   
    
            number - MAX(deduct) OVER(PARTITION BY lastRelevantId ORDER BY id ROWS UNBOUNDED PRECEDING ) AS ReNumber,
    
        FROM AuxTable2
    

    我认为这运行得更快,但并没有更短。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-11-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多