基于具有序列的条件递增计数器

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于具有序列的条件递增计数器相关的知识,希望对你有一定的参考价值。

我试图根据一些标准自动增加SELECT查询的计数器。

当BatchID和Reference值发生变化时,应该有一个计数器增加1。

在BatchID保持不变的情况下,它应该只使用2个潜在的唯一数字,一个用于有参考值的地方,另一个用于没有参考值的地方。我在下面的示例数据中添加了一个列,其中包含ExpectedResultCounter,它显示了我期望的结果。

我已成功通过以下查询获得编号:

select 
    NEXT VALUE FOR seqAutoNumber OVER (ORDER BY RowFilter) AS ID,
    *
from
(
    select
        DENSE_RANK() OVER (ORDER BY BatchID, ContainsValue) AS RowFilter,
        ExpectedResultCounter,
        BatchID,
        Reference
    from
    (
        select CASE WHEN Reference is null then 1 else 0 END as ContainsValue, * 
        from   #Temp
    ) a
) 
b
order by 
    BatchID, 
    Reference

但是我不能让序列号与RowFilter匹配相同的编号,并且在给定的情况下它会坚持相同的编号。如果我尝试PARTITION BY,我会收到以下错误:

Msg 11716, Level 15, State 1, Line 41
NEXT VALUE FOR function does not support the PARTITION BY clause.

有人有任何想法吗?是否可以使用序列重复使用相同的数字?如果没有,那么在每次运行查询时不需要重新使用先前执行的先前数字时,需要生成一组新数字来解决此问题的好方法是什么。

CREATE SEQUENCE [dbo].[seqAutoNumber] 
 AS [int]
 START WITH 1
 INCREMENT BY 1
 MINVALUE 1
 MAXVALUE 999999
 CYCLE 
 CACHE 
GO

Create Table #Temp
(
    Reference varchar(50) NULL,
    BatchID int,
    ExpectedResultCounter int
)

insert into #Temp
(
    Reference,
    BatchID,
    ExpectedResultCounter
)
SELECT 'P044276-8',21416,1 UNION ALL
SELECT 'E3723492-6',21419,2 UNION ALL
SELECT 'A62723432-1',21419,2 UNION ALL
SELECT 'P0402343250-4',21419,2 UNION ALL
SELECT 'P2602348-4',21419,2 UNION ALL
SELECT 'B0110662-2',21419,2 UNION ALL
SELECT 'P3234977-7',21419,2 UNION ALL
SELECT NULL,21419,3 UNION ALL
SELECT NULL,21419,3 UNION ALL
SELECT 'P382342391-1',21419,2 UNION ALL
SELECT NULL,21419,3 UNION ALL
SELECT 'Q234234234-3',21419,2 UNION ALL
SELECT 'E37234234-6',21468,4 UNION ALL
SELECT 'A6232432-1',21468,4 UNION ALL
SELECT 'P04023423450-4',21468,4 UNION ALL
SELECT 'P2623432408-4',21468,4 UNION ALL
SELECT 'B0023423462-2',21468,4 UNION ALL
SELECT NULL,21468,5 UNION ALL
SELECT NULL,21468,5 UNION ALL
SELECT NULL,21468,5 UNION ALL
SELECT NULL,21468,5 UNION ALL
SELECT NULL,21468,5

select * from #Temp
order by ExpectedResultCounter

drop table #Temp
答案

你不能重复使用SEQUENCE号码。使用它有很多限制和限制。你可以参考NEXT VALUE FOR (Transact-SQL)

您的要求的一个解决方法是将结果放入临时表中。

然后根据BatchID分组生成ID并更新回临时表

update  t
set     ResultCounter = r.ID
from
(
      select   BatchID, ID = NEXT VALUE FOR seqAutoNumber OVER (ORDER BY BatchID) 
      from     #Temp
      group by BatchID
) r
inner join #Temp t  on  r.BatchID   = t.BatchID

编辑1:

update  t
set     ResultCounter = r.ID
from
(
    select  BatchID, 
            RefIsNull = CASE WHEN Reference IS NULL THEN 1 ELSE 0 END, 
            ID = NEXT VALUE FOR seqAutoNumber OVER (ORDER BY BatchID) 
    from    #Temp
    group by BatchID, CASE WHEN Reference IS NULL THEN 1 ELSE 0 END
) r
inner join #Temp t  on  r.BatchID   = t.BatchID
                    and (
                            (r.RefIsNull    = 1 and t.Reference is null)
                        or  (r.RefIsNull    = 0 and t.Reference is not null)
                        )
另一答案

我认为你在那里是99%。有时必须将事物分成多个步骤(子查询)以使SQL执行您希望它执行的操作。

下面的查询具有基于您的样本数据的列ExpectedResultCounter,以及作为查询中逻辑的最终结果的列ResultCounter

回答:

select a.Reference
, a.BatchID
, a.ExpectedResultCounter
, b.ResultCounter
from #temp as a
inner join 
    (
    select distinct t.BatchID
    , iif(t.reference is null, 1, 0) as is_ref_null_flg
    , dense_rank() over (order by t.BatchId, iif(t.reference is null, 1, 0)) as ResultCounter
    from #temp as t
    ) as b
    on a.BatchID = b.BatchID
    and iif(a.reference is null, 1, 0) = b.is_ref_null_flg
order by b.ResultCounter
, a.Reference

更新:

为了利用您在问题中定义的sequence对象,您必须使用下面的逻辑。您的定义将确保您每次都不会返回相同的ResultCounter值。

select c.BatchID
, c.is_ref_null_flg
, next value for dbo.seqAutoNumber over (order by c.ResultCounterPrelim) as ResultCounter
into #temp_step_one
from (
    select distinct t.BatchID
    , iif(t.reference is null, 1, 0) as is_ref_null_flg
    , dense_rank() over (order by t.BatchId, iif(t.reference is null, 1, 0)) as ResultCounterPrelim
    from #temp as t
    ) as c

select a.Reference
, a.BatchID
, a.ExpectedResultCounter
, b.ResultCounter
from #temp as a
inner join #temp_step_one as b on a.BatchID = b.BatchID
                              and iif(a.reference is null, 1, 0) = b.is_ref_null_flg
order by b.ResultCounter
, a.Reference

这个修改后的答案被分成两个查询,而不是使用子查询,因为next value for语法不能在子查询中使用(根据documentation)。

旁注:根据所涉及的数据量,join的性能可能有点粗糙。

以上是关于基于具有序列的条件递增计数器的主要内容,如果未能解决你的问题,请参考以下文章

[ css 计数器 counter ] css中counter计数器(序列数字字符自动递增)应用问题讲解及实例演示

pyspark中基于条件对多列进行分组的累积和函数

基于列值变化的 T-SQL 递增计数器

[编程题] lc:[674 最长连续递增序列

条件递增

动态规划之最大递增子序列