【问题标题】:Is a cursor/recursion the only possible option here? Set based logic based on certain lag conditions游标/递归是这里唯一可能的选择吗?根据某些滞后条件设置基于逻辑
【发布时间】:2020-01-24 20:19:42
【问题描述】:

我正在使用 SQL Server,我有一个包含 3 列(时间序列)data 的表,日期以 hour 开头,AwardStatus

大部分奖励状态是随机生成的。可以有两种选择,Awarded 或 Not Awarded。

但是,业务要求是,如果状态为 NotAwarded,我们必须连续打印 3 行“NotAwarded”,如果状态为 Awarded,则必须连续打印 4 行。

目标:新列ShouldBe 详细信息。

一旦满足最低要求,它就会检查当前行的AwardStatus 并继续覆盖逻辑。

问题:在没有任何类型的游标/循环的情况下,这在 SQL 中是否可行?

以下图为例。

这是一个例子:

AwardStatusMinimum  3       
AwardStatusMaximum  4       

Date    Hour    AwardStatus ShouldBe
--------------------------------------
1/1/2019    1   NotAwarded  NotAwarded
1/1/2019    2   NotAwarded  NotAwarded
1/1/2019    3   Awarded     NotAwarded
1/1/2019    4   Awarded     Awarded
1/1/2019    5   NotAwarded  Awarded
1/1/2019    6   NotAwarded  Awarded
1/1/2019    7   Awarded     Awarded
1/1/2019    8   NotAwarded  NotAwarded
1/1/2019    9   Awarded     NotAwarded
1/1/2019    10  Awarded     NotAwarded

【问题讨论】:

标签: sql sql-server


【解决方案1】:

既然提到了递归。

这是一个使用递归 CTE 的解决方案。

样本数据:

CREATE TABLE Table1 (
  [Date] DATETIME NOT NULL, 
  [Hour] INT NOT NULL, 
  [AwardStatus] VARCHAR(10)
);

INSERT INTO Table1
 ([Date], [Hour], [AwardStatus])
VALUES
 ('2019-01-01', 1, 'NotAwarded'),      
 ('2019-01-01', 2, 'NotAwarded'),     
 ('2019-01-01', 3, 'Awarded'),
 ('2019-01-01', 4, 'Awarded'),
 ('2019-01-01', 5, 'NotAwarded'),
 ('2019-01-01', 6, 'NotAwarded'),
 ('2019-01-01', 7, 'Awarded'),
 ('2019-01-01', 8, 'NotAwarded'),
 ('2019-01-01', 9, 'Awarded'),
 ('2019-01-01', 10, 'Awarded');

查询:

;with CTE_DATA AS 
(
  select *
  , dense_rank() 
    over (order by cast([Date] as date)) as grp
  , row_number() 
    over (partition by cast([Date] as date) order by [Hour]) as rn
  from Table1
)
, RCTE_AWARDS as
(
  select [Date], [Hour]
  , AwardStatus
  , grp
  , rn
  , 1 as Lvl
  , AwardStatus AS CalcStatus
  from CTE_DATA
  where rn = 1
  
  union all
  
  select t.[Date], t.[Hour]
  , t.AwardStatus
  , t.grp
  , t.rn
  , case
    when (c.lvl < 3)
      or (c.lvl < 4 and c.CalcStatus = 'Awarded')
    then c.lvl+1
    else 1
    end
  , case 
    when (c.lvl = 3 and c.CalcStatus = 'NotAwarded')
      or (c.lvl = 4)
    then t.AwardStatus
    else c.CalcStatus
    end
  from RCTE_AWARDS c
  join CTE_DATA t 
    on t.grp = c.grp
   and t.rn = c.rn + 1
)
select [Date], [Hour], AwardStatus
, CalcStatus AS NewAwardStatus
from RCTE_AWARDS
order by [Date], [Hour]

GO
日期 |小时 |奖项状态 |新奖状态 :------------------------ | ---: | :------------ | :------------- 2019-01-01 00:00:00.000 | 1 |未获奖 |未获奖 2019-01-01 00:00:00.000 | 2 |未获奖 |未获奖 2019-01-01 00:00:00.000 | 3 |获奖 |未获奖 2019-01-01 00:00:00.000 | 4 |获奖 |获奖 2019-01-01 00:00:00.000 | 5 |未获奖 |获奖 2019-01-01 00:00:00.000 | 6 |未获奖 |获奖 2019-01-01 00:00:00.000 | 7 |获奖 |获奖 2019-01-01 00:00:00.000 | 8 |未获奖 |未获奖 2019-01-01 00:00:00.000 | 9 |获奖 |未获奖 2019-01-01 00:00:00.000 | 10 |获奖 |未获奖

dbfiddle here

的测试

【讨论】:

    【解决方案2】:

    这允许您在不使用光标的情况下进行操作。

     declare @date date
     declare @hour int
     declare @CurrentStatus varchar(50)
    
     set @CurrentStatus=''
    
     while exists(select * from Awards where ShouldBe is null)
     begin
         select top 1 @date=[date], @hour=[hour] , @CurrentStatus=AwardStatus
         from Awards 
         where [ShouldBe] is null 
         order by [date],[hour]
    
         if(@CurrentStatus='Awarded')
         begin
             update top(4) Awards
             set ShouldBe=@CurrentStatus
             where [date]=@date and [hour]>=@hour
         end
         else 
             begin
             update top(3) Awards
             set ShouldBe=@CurrentStatus
             where [date]=@date and [hour]>=@hour
         end
    
     end
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-03-27
      • 2016-10-17
      • 1970-01-01
      • 2019-10-02
      • 1970-01-01
      • 1970-01-01
      • 2020-09-11
      相关资源
      最近更新 更多