【问题标题】:SQL Server get multiple next values for sequenceSQL Server 获取序列的多个下一个值
【发布时间】:2020-09-08 13:51:43
【问题描述】:

在 SQL Server 中,您可以像这样从序列中选择下一个值:

select next value for MY_SEQ

如果您不禁止选择要从中选择的表,则对于每一行都会输出下一个值:

select next value for MY_SEQ 
from MY_TABLE

[2020-09-08 15:47:34] 350 rows retrieved starting from 1 in 102 ms (execution: 62 ms, fetching: 40 ms)

如何为序列选择下一个 n 值?

在 Oracle 中,这看起来像这样:

select MY_SEQ.nextval
from (
         select level
         from dual
         connect by level < 10
     )

我尝试过这样的事情:

select top(10) next value for MY_SEQ

但结果是:

[S0001][11739] 如果设置了 ROWCOUNT 选项,或者查询包含 TOP 或 OFFSET,则无法使用 NEXT VALUE FOR 函数。

我想我可以创建一个包含 n 行的临时表并从中进行选择,但这不是一个特别优雅的解决方案。

【问题讨论】:

    标签: sql sql-server tsql-sequence


    【解决方案1】:

    我认为您正在寻找系统存储过程“sp_sequence_get_range”

    要从序列中获取接下来的 10 个值,应该是这样的

    DECLARE @range_first_value_output sql_variant ;  
     
    EXEC sys.sp_sequence_get_range  
    @sequence_name = N'MY_SEQ'  
    , @range_size = 10  
    , @range_first_value = @range_first_value_output OUTPUT ;  
     
    SELECT @range_first_value_output AS FirstNumber;  
    

    【讨论】:

    • 这似乎仍然只返回一个值
    【解决方案2】:

    您可以在获取序列值之前提取正确的行数

    DECLARE @N INT = 100;
    
    SELECT next value FOR MY_SEQ 
    FROM (
        SELECT 1 X
        FROM FN_NUMBERS(@N)
    ) X
    
    
    CREATE FUNCTION [dbo].[FN_NUMBERS](
         @MAX INT
    )
    RETURNS @N TABLE (N INT NOT NULL PRIMARY KEY)  
    BEGIN
         WITH
           Pass0 as (select '1' as C union all select '1'),       --2 rows
           Pass1 as (select '1' as C from Pass0 as A, Pass0 as B),--4 rows
           Pass2 as (select '1' as C from Pass1 as A, Pass1 as B),--16 rows
           Pass3 as (select '1' as C from Pass2 as A, Pass2 as B),--256 rows
           Pass4 as (select TOP (@MAX) '1' as C from Pass3 as A, Pass3 as B)    --65536 rows
           ,Tally as (select TOP (@MAX) '1' as C from Pass4 as A, Pass2 as B, Pass1 as C)  --4194304 rows
           --,Tally as (select TOP (@MAX) '1' as C from Pass4 as A, Pass3 as B)               --16777216 rows
           --,Tally as (select TOP (@MAX) '1' as C from Pass4 as A, Pass4 as B)               --4294836225 rows
         INSERT INTO @N
         SELECT TOP (@MAX) ROW_NUMBER() OVER(ORDER BY C) AS N
         FROM Tally
         RETURN
    END
    

    您可以简单地使用 sys.objects 而不是计数表

    SELECT next value FOR MY_SEQ 
    FROM (
        SELECT TOP (@N) 1 X
        FROM sys.objects o1, sys.objects o2, sys.objects o3
    ) X
    

    【讨论】:

    • 因此我需要一个表,虽然它保证有我需要的行数,但这似乎也不优雅
    • 使用计数表
    • FN_NUMBESR 函数的选项似乎可以工作,但对于手头的问题似乎有点过于复杂
    • sys.objects 的解决方案似乎工作正常,而且它应该在大多数时间工作是有道理的,但它仍然看起来很hacky
    • 您的数据库中应该始终有一个 FN_NUMBERS,您会经常使用它。但是您可以简单地多次使用 sys.objects 来获得相同的结果
    【解决方案3】:

    为什么不直接用下一个值填充表变量?

    declare @next_values table (next_value int)
    declare @i int = 1
    
    while @i <= 10 
    begin
      insert into @next_values (next_value)
                  select next value for MY_SEQ from MY_TABLE
    
      set @i = @i + 1
    end
    
    -- We return those values 
    select * from @next_values
    

    【讨论】:

    • 我无法想象进行 n 手动选择查询会特别高效
    • 这不是一个游标,只是一个循环,我严重怀疑你会看到从循环而不是选择调用 n 次 next value 会有很大的不同。您仍然需要对该伪函数进行相同的 n 次调用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-20
    • 1970-01-01
    • 1970-01-01
    • 2014-01-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多