基于带有条件的条件递增计数器

时间:2018-11-24 00:57:23

标签: sql-server tsql

我正在尝试根据某些条件自动为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

2 个答案:

答案 0 :(得分:2)

您不能重复使用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)
                        )

答案 1 :(得分:1)

我认为您的存在率为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的性能可能有些粗糙。