SQL如何更改接下来的2条记录中的列值

问题描述

我有一个表,其中包含两列,一列名为“ ID”,另一列为“ FLG”。我需要添加基于列“ FLG”的另一个列“ FLG1”。

过程逻辑是,如果此表中的一条记录为“ Y”,则接下来的2条记录在“ FLG1”中应均为“ N”,如果为“ N”,则只需将其保留在“ FLG1”中

例如:

对于ID = 2和FLG = Y的记录,则该记录的FLG1应该为“ Y”,但是对于接下来的2条记录(ID = 3,4),FLG1应该为“ N”,尽管它们的FLG为“是”。

预期结果如下:

enter image description here

我已经尝试了很多天,但都失败了,并且我不需要存储过程,我只想要用于实现的SQL查询脚本。

以下是数据的脚本:

 select 1 as ID,'N' as FLG from dual union all
 select 2 as ID,'Y' as FLG from dual union all
 select 3 as ID,'Y' as FLG from dual union all
 select 4 as ID,'Y' as FLG from dual union all
 select 5 as ID,'Y' as FLG from dual union all
 select 6 as ID,'Y' as FLG from dual union all
 select 7 as ID,'Y' as FLG from dual union all
 select 8 as ID,'Y' as FLG from dual union all
 select 9 as ID,'N' as FLG from dual union all
 select 10 as ID,'Y' as FLG from dual union all
 select 11 as ID,'N' as FLG from dual union all
 select 12 as ID,'Y' as FLG from dual union all
 select 13 as ID,'N' as FLG from dual union all
 select 14 as ID,'N' as FLG from dual union all
 select 15 as ID,'N' as FLG from dual union all
 select 16 as ID,'Y' as FLG from dual union all
 select 17 as ID,'N' as FLG from dual union all
 select 18 as ID,'N' as FLG from dual union all
 select 19 as ID,'N' as FLG from dual union all
 select 20 as ID,'N' as FLG from dual union all
 select 21 as ID,'N' as FLG from dual union all
 select 22 as ID,'Y' as FLG from dual union all
 select 23 as ID,'Y' as FLG from dual

解决方法

使用LAG查看前两行。然后使用“ CASE WHEN”决定要显示什么。
select 
  id,flg,case
    when lag(flg) over(order by id) = 'Y' or lag(flg,2) over(order by id) = 'Y' then 'N'
    else flg
  end as flg1
from mytable
order by id;

您的描述有误。您想遍历要在SQL中使用递归查询的行。

首先为您的行编号,因为表的ID中总会有空格。然后,使用递归查询在各行之间循环。我认为这是解决此问题的直接方法。

with numbered as (select t.*,row_number() over (order by id) as rn from mytable t),cte (id,flg1,prev_flg1,rn) as
(
  select id,null,rn from numbered where rn = 1
  union all
  select
    t.id,t.flg,case
      when cte.flg1 = 'Y' or cte.prev_flg1 = 'Y' then 'N'
      else t.flg
    end,cte.flg1,t.rn
  from cte 
  join numbered t on t.rn = cte.rn + 1
)
select id,flg1
from cte
order by id;
,

您需要依次遍历各行并计算是否显示该行,然后才能计算是否在其后显示行;这意味着您需要递归(或分层)查询。

您需要将行分成连续的三行,以使每个三元组都以FLG = 'Y'行开头,然后是所有不在该三元组中或在三元组的第二行或第三行中的行将具有FLG1的{​​{1}}值。

赞:

N

对于您的示例数据,输出:

ID | FLG | FLG1
-: | :-- | :---
 1 | N   | N   
 2 | Y   | Y   
 3 | Y   | N   
 4 | Y   | N   
 5 | Y   | Y   
 6 | Y   | N   
 7 | Y   | N   
 8 | Y   | Y   
 9 | N   | N   
10 | Y   | N   
11 | N   | N   
12 | Y   | Y   
13 | N   | N   
14 | N   | N   
15 | N   | N   
16 | Y   | Y   
17 | N   | N   
18 | N   | N   
19 | N   | N   
20 | N   | N   
21 | N   | N   
22 | Y   | Y   
23 | Y   | N   

db 提琴here

,

在我对该问题的误解中,这是一个非常简单的差距与岛屿问题。请参见下面的编辑以获取改进的答案。我建议使用行号的不同来定义孤岛。然后,标志的定义只是检查'Y'值的每组上的行号:

select id,(case when flg = 'Y' and
                  mod(row_number() over (partition by flg,seqnum - seqnum_2 order by id),3) = 1
             then 'Y'
             else 'N'
        end) as flg1
from (select t.*,row_number() over (order by id) as seqnum,row_number() over (partition by flg order by id) as seqnum_2
      from t
     ) t
order by id;

Here是db 小提琴。

如果要更新标志,我建议使用merge

注意:我还希望它比递归CTE方法更快(也许更快)。

编辑:

亚历克斯提出了一个非常好的观点。我认为这需要递归CTE。如果您有大量数据,则可以通过将数据分成多个连续'N'的组来进行优化。您的问题没有提到数据大小。

我会这样处理:

with tt as (
       select t.*,row_number() over (order by id) as seqnum
       from t
     ),cte (seqnum,id,counter) as (
      select seqnum,(case when flg = 'Y' then 1 else 0 end)
      from tt
      where seqnum = 1
      union all
      select tt.seqnum,tt.id,tt.flg,(case when cte.counter in (1,2) then 'N'
                   when tt.flg = 'Y' then 'Y'
                   else 'N'
              end),2) then cte.counter + 1
                   when tt.flg = 'Y' then 1
                   else 0
              end)
      from cte join
            tt
            on tt.seqnum = cte.seqnum + 1
     )
select *
from cte;

基本上,它遍历数据并找到第一个'Y'。此时,它将计数器设置为1。在接下来的两行中,无论标志的值如何,计数器都会递增。然后返回寻找'Y'以重复该过程。

有趣的是,这似乎是使用图灵机实现的非常简单的操作。通常,如何实现这些东西并不明显。

有趣的是,如果将所有标志放在字符串中,则正则表达式可以非常简单地解决问题:

select flgs,substr(regexp_replace(flgs,'Y(..|.$|$)','YNN'),1,length(flgs)) as flg1s
from (select listagg(flg,'') within group (order by id) as flgs
      from t
     ) t;

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...