如何在两个数字之间生成一个数字范围?


141

我有两个数字来自用户输入,例如 10001050

如何使用sql查询在单独的行中生成这两个数字之间的数字?我要这个:

 1000
 1001
 1002
 1003
 .
 .
 1050

Answers:


159

使用VALUES关键字选择非持久性值。然后使用JOIN来生成很多组合(可以扩展以创建成千上万的行以及更多)。

SELECT ones.n + 10*tens.n + 100*hundreds.n + 1000*thousands.n
FROM (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) ones(n),
     (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) tens(n),
     (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) hundreds(n),
     (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) thousands(n)
WHERE ones.n + 10*tens.n + 100*hundreds.n + 1000*thousands.n BETWEEN @userinput1 AND @userinput2
ORDER BY 1

Demo

一个较短的替代方法,不是那么容易理解:

WITH x AS (SELECT n FROM (VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) v(n))
SELECT ones.n + 10*tens.n + 100*hundreds.n + 1000*thousands.n
FROM x ones,     x tens,      x hundreds,       x thousands
ORDER BY 1

Demo


13
这是一个非常优雅的解决方案
Aaron Hudon

9
你能解释一下语法吗?v(n)是多少?
拉菲

2
@Rafi v(n)和数百(n)等是表和列的名称/别名
Twon-ha

106

另一个解决方案是递归CTE:

DECLARE @startnum INT=1000
DECLARE @endnum INT=1050
;
WITH gen AS (
    SELECT @startnum AS num
    UNION ALL
    SELECT num+1 FROM gen WHERE num+1<=@endnum
)
SELECT * FROM gen
option (maxrecursion 10000)

4
不要尝试在视图定义中使用maxrecusion选项。相反,您必须选择SELECT * FROM CTE_VIEW OPTION(MAXRECURSION 10000)-如果您的客户端应用程序想按原样使用视图,则有问题。
2015年

4
最大maxrecursion设置为32767(在SQL Server 2012中)。
BProv

4
只是为了澄清,如果你需要超过32767递归,那么它可以被设置为0,表示NOMAX,
二队

2
这是此答案的演示
造口

7
我将此答案与其他答案进行了比较,执行计划显示该答案(查询成本最低,)最快。
造口术'18

39
SELECT DISTINCT n = number 
FROM master..[spt_values] 
WHERE number BETWEEN @start AND @end

Demo

请注意,此表的最大值为2048,因为这时数字之间存在间隙。

这是使用系统视图的一种更好的方法(自SQL-Server 2005起):

;WITH Nums AS
(
  SELECT n = ROW_NUMBER() OVER (ORDER BY [object_id]) 
  FROM sys.all_objects 

)
SELECT n FROM Nums 
WHERE n BETWEEN @start AND @end
ORDER BY n;

Demo

或使用自定义数字表。感谢Aaron Bertrand,我建议阅读整篇文章:生成没有循环的集合或序列


2
@ user3211705:注意我的编辑,该表最大为2048。我建议阅读整篇文章。
蒂姆·施密特2014年

3
认为您可以增加WHERE type = 'P'和避免SELECT DISTINCT
Salman A

1
您的第一个“演示”链接不断告诉我String index out of range: 33
slartidan 2015年

1
你是对的。但这似乎是SqlFiddle的问题。它可以在您的数据库中工作吗?
蒂姆·施密特

4
快速说明,这种跨数据库查询不适用于SQL Azure
Kieren Johnstone,

33

我最近写了这个内联表值函数来解决这个问题。除内存和存储空间外,不受范围限制。它不访问任何表,因此一般不需要磁盘读写。它在每次迭代中以指数方式添加联接值,因此即使在很大范围内也非常快。它在五秒钟内在我的服务器上创建了一千万条记录。它也可以使用负值。

CREATE FUNCTION [dbo].[fn_ConsecutiveNumbers]
(   
    @start int,
    @end  int
) RETURNS TABLE 
RETURN 

select
    x268435456.X
    | x16777216.X
    | x1048576.X
    | x65536.X
    | x4096.X
    | x256.X
    | x16.X
    | x1.X
    + @start
     X
from
(VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13),(14),(15)) as x1(X)
join
(VALUES (0),(16),(32),(48),(64),(80),(96),(112),(128),(144),(160),(176),(192),(208),(224),(240)) as x16(X)
on x1.X <= @end-@start and x16.X <= @end-@start
join
(VALUES (0),(256),(512),(768),(1024),(1280),(1536),(1792),(2048),(2304),(2560),(2816),(3072),(3328),(3584),(3840)) as x256(X)
on x256.X <= @end-@start
join
(VALUES (0),(4096),(8192),(12288),(16384),(20480),(24576),(28672),(32768),(36864),(40960),(45056),(49152),(53248),(57344),(61440)) as x4096(X)
on x4096.X <= @end-@start
join
(VALUES (0),(65536),(131072),(196608),(262144),(327680),(393216),(458752),(524288),(589824),(655360),(720896),(786432),(851968),(917504),(983040)) as x65536(X)
on x65536.X <= @end-@start
join
(VALUES (0),(1048576),(2097152),(3145728),(4194304),(5242880),(6291456),(7340032),(8388608),(9437184),(10485760),(11534336),(12582912),(13631488),(14680064),(15728640)) as x1048576(X)
on x1048576.X <= @end-@start
join
(VALUES (0),(16777216),(33554432),(50331648),(67108864),(83886080),(100663296),(117440512),(134217728),(150994944),(167772160),(184549376),(201326592),(218103808),(234881024),(251658240)) as x16777216(X)
on x16777216.X <= @end-@start
join
(VALUES (0),(268435456),(536870912),(805306368),(1073741824),(1342177280),(1610612736),(1879048192)) as x268435456(X)
on x268435456.X <= @end-@start
WHERE @end >=
    x268435456.X
    | isnull(x16777216.X, 0)
    | isnull(x1048576.X, 0)
    | isnull(x65536.X, 0)
    | isnull(x4096.X, 0)
    | isnull(x256.X, 0)
    | isnull(x16.X, 0)
    | isnull(x1.X, 0)
    + @start

GO

SELECT X FROM fn_ConsecutiveNumbers(5, 500);

对于日期和时间范围也很方便:

SELECT DATEADD(day,X, 0) DayX 
FROM fn_ConsecutiveNumbers(datediff(day,0,'5/8/2015'), datediff(day,0,'5/31/2015'))

SELECT DATEADD(hour,X, 0) HourX 
FROM fn_ConsecutiveNumbers(datediff(hour,0,'5/8/2015'), datediff(hour,0,'5/8/2015 12:00 PM'));

您可以对其使用交叉应用联接,以根据表中的值拆分记录。因此,例如,要为表中的时间范围内的每一分钟创建一条记录,您可以执行以下操作:

select TimeRanges.StartTime,
    TimeRanges.EndTime,
    DATEADD(minute,X, 0) MinuteX
FROM TimeRanges
cross apply fn_ConsecutiveNumbers(datediff(hour,0,TimeRanges.StartTime), 
        datediff(hour,0,TimeRanges.EndTime)) ConsecutiveNumbers

1
哇,最初的查询是FAST。比上面发布的CLR解决方案快得多。谢谢!
Derreck Dean 2016年

1
很好-我的客户端仍在使用SQL Server 2008,这正是我所需要的!非常聪明!
STLDev '18

1
它适用于1-100,但随后失败。即使您生成5-500的示例对我也不起作用,它显示
5、21

3
如果您希望对它进行排序,则必须按子句添加订单:SELECT X FROM fn_ConsecutiveNumbers(5, 500) ORDER BY X;
Brian Pressler,

29

我使用的最佳选择如下:

DECLARE @min bigint, @max bigint
SELECT @Min=919859000000 ,@Max=919859999999

SELECT TOP (@Max-@Min+1) @Min-1+row_number() over(order by t1.number) as N
FROM master..spt_values t1 
    CROSS JOIN master..spt_values t2

我已经使用它生成了数百万条记录,并且效果很好。


2
这是这里最优雅的解决方案,但是我认为许多人都很难理解它(我一直在使用master.sys.all_columns进行此操作)。@STLDeveloper,是的,它适用于2008年及更高版本。
Cetin Basoz

13

它为我工作!

select top 50 ROW_NUMBER() over(order by a.name) + 1000 as Rcount
from sys.all_objects a

2
好的一线-但要注意最大行数取决于sys.all_objects-对于小于2000的小范围项目,这不是问题。不确定是否会有权限问题?非常适合快速生成一批测试数据。
freen-m

@freedomn -m增加最大行数的一种方法是执行自交叉连接。select top 50 ROW_NUMBER() over(order by a.name) + 1000 as Rcount from sys.all_objects a, sys.all_objects b。在以前只能生成2384行的地方,现在可以生成5683456行。
Klicker

9

最好的方法是使用递归ctes。

declare @initial as int = 1000;
declare @final as int =1050;

with cte_n as (
    select @initial as contador
    union all
    select contador+1 from cte_n 
    where contador <@final
) select * from cte_n option (maxrecursion 0)

礼炮。


1
这非常有用。我修改了代码,因此可以插入100.000行。用我的解决方案花了大约13分钟;用你的,花了五秒钟。Muchísimasgracias。
克苏鲁

2
实际上,递归CTE是最差的计算方法之一。它们甚至可以在事务中被While循环打败,而While循环将产生更少的读取。cCTE方法(级联CTE,最初由Itizik Ben-Gan提出)要快得多,并且产生零读数。
杰夫·摩登

9
declare @start int = 1000
declare @end    int =1050

;with numcte  
AS  
(  
  SELECT @start [SEQUENCE]  
  UNION all  
  SELECT [SEQUENCE] + 1 FROM numcte WHERE [SEQUENCE] < @end 
)      
SELECT * FROM numcte

1
这与@Jayvee的答案不同吗?
诺埃尔(Noel)

1
是的,在这种情况下,其编号为num + 1 <1050,只能打印到1049。
Sowbarani Karthikeyan '18 -10-3

2
对现有答案必不可少的编辑(或评论)将比全新答案提供更多的价值。
诺埃尔(Noel)

7

如果您在服务器中安装CLR程序集没有问题,那么一个不错的选择是在.NET中编写表值函数。这样,您可以使用简单的语法,使其易于与其他查询联接,并且由于结果是流式传输,因此不会浪费内存。

创建一个包含以下类的项目:

using System;
using System.Collections;
using System.Data;
using System.Data.Sql;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;

namespace YourNamespace
{
   public sealed class SequenceGenerator
    {
        [SqlFunction(FillRowMethodName = "FillRow")]
        public static IEnumerable Generate(SqlInt32 start, SqlInt32 end)
        {
            int _start = start.Value;
            int _end = end.Value;
            for (int i = _start; i <= _end; i++)
                yield return i;
        }

        public static void FillRow(Object obj, out int i)
        {
            i = (int)obj;
        }

        private SequenceGenerator() { }
    }
}

将程序集放在服务器上的某个位置并运行:

USE db;
CREATE ASSEMBLY SqlUtil FROM 'c:\path\to\assembly.dll'
WITH permission_set=Safe;

CREATE FUNCTION [Seq](@start int, @end int) 
RETURNS TABLE(i int)
AS EXTERNAL NAME [SqlUtil].[YourNamespace.SequenceGenerator].[Generate];

现在您可以运行:

select * from dbo.seq(1, 1000000)

1
我尝试了这种解决方案,并且效果很好,只是速度不是很快。如果您仅生成1,000个数字,或者可能生成10,000个数字,那将相当快。如果您像我一样并且必须生成数十亿个数字,那么与SQL CLR相比,Brian Pressler的以下解决方案出奇地快。
Derreck Dean 2016年

2
@DerreckDean你是对的。我认为他是最好的解决方案,因为它易于创建和使用(并且您说得很快)。就我而言,我已经有一个用于连接字符串的程序集,因此我只在其中添加了它。
AlexDev

1
我也有一个现有的程序集,并尝试了两种方法。我正在生成不确定数量的数字以添加到日期中(基本上,我重新创建了SQL Server代理计划程序以为内部应用程序生成日期,并且100级别的递归不会因为生成多个日期时间,可能会减少到第二个。),因此我能够从该线程彻底测试多个解决方案。感谢您的贡献!
Derreck院长

7

没什么新奇的,但我改写了Brian Pressler解决方案,以使其更轻松,它对某人可能有用(即使这只是我的未来):

alter function [dbo].[fn_GenerateNumbers]
(   
    @start int,
    @end  int
) returns table
return

with 
b0 as (select n from (values (0),(0x00000001),(0x00000002),(0x00000003),(0x00000004),(0x00000005),(0x00000006),(0x00000007),(0x00000008),(0x00000009),(0x0000000A),(0x0000000B),(0x0000000C),(0x0000000D),(0x0000000E),(0x0000000F)) as b0(n)),
b1 as (select n from (values (0),(0x00000010),(0x00000020),(0x00000030),(0x00000040),(0x00000050),(0x00000060),(0x00000070),(0x00000080),(0x00000090),(0x000000A0),(0x000000B0),(0x000000C0),(0x000000D0),(0x000000E0),(0x000000F0)) as b1(n)),
b2 as (select n from (values (0),(0x00000100),(0x00000200),(0x00000300),(0x00000400),(0x00000500),(0x00000600),(0x00000700),(0x00000800),(0x00000900),(0x00000A00),(0x00000B00),(0x00000C00),(0x00000D00),(0x00000E00),(0x00000F00)) as b2(n)),
b3 as (select n from (values (0),(0x00001000),(0x00002000),(0x00003000),(0x00004000),(0x00005000),(0x00006000),(0x00007000),(0x00008000),(0x00009000),(0x0000A000),(0x0000B000),(0x0000C000),(0x0000D000),(0x0000E000),(0x0000F000)) as b3(n)),
b4 as (select n from (values (0),(0x00010000),(0x00020000),(0x00030000),(0x00040000),(0x00050000),(0x00060000),(0x00070000),(0x00080000),(0x00090000),(0x000A0000),(0x000B0000),(0x000C0000),(0x000D0000),(0x000E0000),(0x000F0000)) as b4(n)),
b5 as (select n from (values (0),(0x00100000),(0x00200000),(0x00300000),(0x00400000),(0x00500000),(0x00600000),(0x00700000),(0x00800000),(0x00900000),(0x00A00000),(0x00B00000),(0x00C00000),(0x00D00000),(0x00E00000),(0x00F00000)) as b5(n)),
b6 as (select n from (values (0),(0x01000000),(0x02000000),(0x03000000),(0x04000000),(0x05000000),(0x06000000),(0x07000000),(0x08000000),(0x09000000),(0x0A000000),(0x0B000000),(0x0C000000),(0x0D000000),(0x0E000000),(0x0F000000)) as b6(n)),
b7 as (select n from (values (0),(0x10000000),(0x20000000),(0x30000000),(0x40000000),(0x50000000),(0x60000000),(0x70000000)) as b7(n))

select s.n
from (
    select
          b7.n
        | b6.n
        | b5.n
        | b4.n
        | b3.n
        | b2.n
        | b1.n
        | b0.n
        + @start
         n
    from b0
    join b1 on b0.n <= @end-@start and b1.n <= @end-@start
    join b2 on b2.n <= @end-@start
    join b3 on b3.n <= @end-@start
    join b4 on b4.n <= @end-@start
    join b5 on b5.n <= @end-@start
    join b6 on b6.n <= @end-@start
    join b7 on b7.n <= @end-@start
) s
where @end >= s.n

GO

1
我相信您已经将漂亮算法的本质提炼为一些彻头彻尾的漂亮代码。
粘土

1
结果以奇怪但不混乱的顺序排列。在5到500的范围内对其进行测试。它会返回5,21,37,...,245,6,22,...您知道订购会如何影响性能?基于的解决方案ROW_NUMBER()没有这个问题。
Przemyslaw Remin,

1
我不是专家,但直觉上我想SQL Server将需要将所有结果放入内存中并在返回之前对它们进行排序,以便更多的内存使用和延迟的响应,而不是随即将结果流化。
Guillaume86

6

2年后,但是我发现我遇到了同样的问题。这是我解决的方法。(已编辑以包含参数)

DECLARE @Start INT, @End INT
SET @Start = 1000
SET @End = 1050

SELECT  TOP (@End - @Start+1) ROW_NUMBER() OVER (ORDER BY S.[object_id])+(@Start - 1) [Numbers]
FROM    sys.all_objects S WITH (NOLOCK)

5

通过消除对笛卡尔积的所有引用并ROW_NUMBER()改为使用(比较执行计划),可以在性能方面改善slartidan的答案

SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS n FROM 
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) x1(x),
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) x2(x),
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) x3(x),
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) x4(x),
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) x5(x)
ORDER BY n

将其包装在CTE中,并添加where子句以选择所需的数字:

DECLARE @n1 AS INT = 100;
DECLARE @n2 AS INT = 40099;
WITH numbers AS (
    SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS n FROM 
    (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) x1(x),
    (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) x2(x),
    (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) x3(x),
    (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) x4(x),
    (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) x5(x)
)
SELECT numbers.n
FROM numbers
WHERE n BETWEEN @n1 and @n2
ORDER BY n

1
ROW_NUMBER仅从1开始。如何使用您的方法从零开始?
造口

2
@stomy SELECT ROW_NUMBER() OVER (...) - 1 AS n。在某些情况下,这可能会降低性能。
Salman A

4

以下是几个非常理想且兼容的解决方案:

USE master;

declare @min as int;    set @min = 1000;
declare @max as int;    set @max = 1050;    --null returns all

--  Up to 256 - 2 048 rows depending on SQL Server version
select  isnull(@min,0)+number.number  as  number
FROM    dbo.spt_values  AS  number
WHERE   number."type"                   =   'P'     --integers
    and (   @max                            is null     --return all
        or  isnull(@min,0)+number.number    <=  @max    --return up to max
    )
order by    number
;

--  Up to 65 536 - 4 194 303 rows depending on SQL Server version
select  isnull(@min,0)+value1.number+(value2.number*numberCount.numbers)  as  number
FROM  dbo.spt_values            AS  value1
  cross join  dbo.spt_values    AS  value2
  cross join (  --get the number of numbers (depends on version)
    select  sum(1)  as  numbers
    from    dbo.spt_values
    where   spt_values."type"   =   'P' --integers
  )                             as  numberCount
WHERE   value1."type" = 'P'   --integers
    and value2."type" = 'P'   --integers
    and (   @max    is null     --return all
        or  isnull(@min,0)+value1.number+(value2.number*numberCount.numbers)    
            <=  @max            --return up to max
    )
order by    number
;

1
这是方法在某种程度上比单纯select荷兰国际集团where spt_values.number between @min and @max
underscore_d

2
需要使用Type ='P'过滤器来防止重复编号。使用此过滤器,表格将返回数字0-2047。因此,只要变量在该范围内,“ @ min和@max之间的数字”过滤器将起作用。我的解决方案将允许您在整数范围(-2,147,483,648)-(2,147,483,647)内最多获得2048行。
jumxozizi

1
仅当最大和最小数之间的差小于2048并且一次可以在某个时间点最多记录2048个时,以上逻辑才有用
Smart003

4

我知道我已经迟了4年,但我偶然发现了这个问题的另一个替代答案。速度问题不仅在于预过滤,还在于防止排序。可以强制执行联接顺序,以使笛卡尔积实际上因联接而累加。使用slartidan的答案作为起点:

    WITH x AS (SELECT n FROM (VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) v(n))
SELECT ones.n + 10*tens.n + 100*hundreds.n + 1000*thousands.n
FROM x ones,     x tens,      x hundreds,       x thousands
ORDER BY 1

如果知道所需范围,则可以通过@Upper和@Lower指定它。通过将联接提示REMOTE与TOP结合使用,我们可以只计算想要的值子集,而不会浪费任何内容。

WITH x AS (SELECT n FROM (VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) v(n))
SELECT TOP (1+@Upper-@Lower) @Lower + ones.n + 10*tens.n + 100*hundreds.n + 1000*thousands.n
FROM x thousands
INNER REMOTE JOIN x hundreds on 1=1
INNER REMOTE JOIN x tens on 1=1
INNER REMOTE JOIN x ones on 1=1

联接提示REMOTE强制优化器首先在联接的右侧进行比较。通过将每个联接指定为从最高有效值到最小有效值的REMOTE,联接本身将正确向上计数。无需使用WHERE进行过滤,也无需使用ORDER BY进行排序。

如果要增加范围,则可以继续添加其他连接,这些连接的数量级逐渐增加,只要在FROM子句中按从最高到最低的顺序对它们进行排序即可。

请注意,这是特定于SQL Server 2008或更高版本的查询。


1
确实很好。同样的技术也可以应用于Brian Pressler的出色回答和Guillaume86的出色重写。
粘土

3

这也可以

DECLARE @startNum INT = 1000;
DECLARE @endNum INT = 1050;
INSERT  INTO dbo.Numbers
        ( Num
        )
        SELECT  CASE WHEN MAX(Num) IS NULL  THEN @startNum
                     ELSE MAX(Num) + 1
                END AS Num
        FROM    dbo.Numbers
GO 51


3

指数大小的递归CTE(即使默认为100递归,也可以建立2 ^ 100个数字):

DECLARE @startnum INT=1000
DECLARE @endnum INT=1050
DECLARE @size INT=@endnum-@startnum+1
;
WITH numrange (num) AS (
    SELECT 1 AS num
    UNION ALL
    SELECT num*2 FROM numrange WHERE num*2<=@size
    UNION ALL
    SELECT num*2+1 FROM numrange WHERE num*2+1<=@size
)
SELECT num+@startnum-1 FROM numrange order by num

根据OP,我认为@startnum并且endnum应该由用户输入?
JC

2

我不得不使用类似的方法将图片文件路径插入数据库。下面的查询工作正常:

DECLARE @num INT = 8270058
WHILE(@num<8270284)
begin
    INSERT  INTO [dbo].[Galleries]
    (ImagePath) 
    VALUES 
    ('~/Content/Galeria/P'+CONVERT(varchar(10), @num)+'.JPG')

    SET @num = @num + 1
end

您的代码为:

DECLARE @num INT = 1000
WHILE(@num<1051)
begin
    SELECT @num

    SET @num = @num + 1
end

2

这就是我的工作,它非常快速,灵活,并且没有很多代码。

DECLARE @count  int =   65536;
DECLARE @start  int =   11;
DECLARE @xml    xml =   REPLICATE(CAST('<x/>' AS nvarchar(max)), @count);

; WITH GenerateNumbers(Num) AS
(
    SELECT  ROW_NUMBER() OVER (ORDER BY @count) + @start - 1
    FROM    @xml.nodes('/x') X(T)
)
SELECT  Num
FROM    GenerateNumbers;

请注意,(ORDER BY @count)是虚拟的。它什么也不做,但是ROW_NUMBER()需要一个ORDER BY。

编辑:我意识到原来的问题是要获取从x到y的范围。我的脚本可以像这样修改以获得范围:

DECLARE @start  int =   5;
DECLARE @end    int =   21;
DECLARE @xml    xml =   REPLICATE(CAST('<x/>' AS nvarchar(max)), @end - @start + 1);

; WITH GenerateNumbers(Num) AS
(
    SELECT  ROW_NUMBER() OVER (ORDER BY @end) + @start - 1
    FROM    @xml.nodes('/x') X(T)
)
SELECT  Num
FROM    GenerateNumbers;

1
这非常快速且灵活。很好地满足了我的需求。
AndrewBanjo1968

1
-- Generate Numeric Range
-- Source: http://www.sqlservercentral.com/scripts/Miscellaneous/30397/

CREATE TABLE #NumRange(
    n int
)

DECLARE @MinNum int
DECLARE @MaxNum int
DECLARE @I int

SET NOCOUNT ON

SET @I = 0
WHILE @I <= 9 BEGIN
    INSERT INTO #NumRange VALUES(@I)
    SET @I = @I + 1
END


SET @MinNum = 1
SET @MaxNum = 1000000

SELECT  num = a.n +
    (b.n * 10) +
    (c.n * 100) +
    (d.n * 1000) +
    (e.n * 10000)
FROM    #NumRange a
CROSS JOIN #NumRange b
CROSS JOIN #NumRange c
CROSS JOIN #NumRange d
CROSS JOIN #NumRange e
WHERE   a.n +
    (b.n * 10) +
    (c.n * 100) +
    (d.n * 1000) +
    (e.n * 10000) BETWEEN @MinNum AND @MaxNum
ORDER BY a.n +
    (b.n * 10) +
    (c.n * 100) +
    (d.n * 1000) +
    (e.n * 10000) 

DROP TABLE #NumRange

1

只要某些应用程序表具有行,这仅适用于序列。假设我想要从1..100开始的序列,并具有列(数字或字符串类型)foo.bar的应用程序表dbo.foo:

select 
top 100
row_number() over (order by dbo.foo.bar) as seq
from dbo.foo

尽管dbo.foo.bar出现在order by子句中,但不必具有唯一的甚至是非null的值。

当然,SQL Server 2012具有序列对象,因此该产品中有一个自然的解决方案。


1

这是我想出的:

create or alter function dbo.fn_range(@start int, @end int)  returns table
return
with u2(n) as (
    select n 
    from (VALUES (0),(1),(2),(3)) v(n)
), 
u8(n) as (
    select
        x0.n | x1.n * 4 | x2.n * 16 | x3.n * 64 as n
    from u2 x0, u2 x1, u2 x2, u2 x3
)
select 
    @start + s.n as n
from (
    select
        x0.n | isnull(x1.n, 0) * 256 | isnull(x2.n, 0) * 65536 as n
    from u8 x0 
    left join u8 x1 on @end-@start > 256
    left join u8 x2 on @end-@start > 65536
) s
where s.n < @end - @start

最多生成2 ^ 24个值。连接条件使它可以快速获得较小的值。


1

在我们的DEV服务器上,这花了36秒为我完成。像Brian的答案一样,从查询中着重于过滤范围很重要。BETWEEN仍然尝试在下限之前生成所有初始记录,即使它不需要它们。

declare @s bigint = 10000000
    ,   @e bigint = 20000000

;WITH 
Z AS (SELECT 0 z FROM (VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13),(14),(15)) T(n)),
Y AS (SELECT 0 z FROM Z a, Z b, Z c, Z d, Z e, Z f, Z g, Z h, Z i, Z j, Z k, Z l, Z m, Z n, Z o, Z p),
N AS (SELECT ROW_NUMBER() OVER (PARTITION BY 0 ORDER BY z) n FROM Y)

SELECT TOP (1+@e-@s) @s + n - 1 FROM N

请注意,ROW_NUMBERbigint,因此我们无法使用任何使用它的方法来遍历2 ^^ 64(== 16 ^^ 16)生成的记录。因此,此查询遵循有关生成值的相同上限。


1

这使用过程代码和表值函数。缓慢,但容易且可预测。

CREATE FUNCTION [dbo].[Sequence] (@start int, @end int)
RETURNS
@Result TABLE(ID int)
AS
begin
declare @i int;
set @i = @start;
while @i <= @end 
    begin
        insert into @result values (@i);
        set @i = @i+1;
    end
return;
end

用法:

SELECT * FROM dbo.Sequence (3,7);
ID
3
4
5
6
7

它是一个表,因此您可以将其与其他数据结合使用。我最经常使用此功能作为对GROUP BY小时,天等联接的左侧,以确保时间值的连续序列。

SELECT DateAdd(hh,ID,'2018-06-20 00:00:00') as HoursInTheDay FROM dbo.Sequence (0,23) ;

HoursInTheDay
2018-06-20 00:00:00.000
2018-06-20 01:00:00.000
2018-06-20 02:00:00.000
2018-06-20 03:00:00.000
2018-06-20 04:00:00.000
(...)

性能令人鼓舞(一百万行16秒),但足以满足多种用途。

SELECT count(1) FROM [dbo].[Sequence] (
   1000001
  ,2000000)
GO


1

我已经开发并使用了很长一段时间的解决方案(在其他人的共享作品中使用了某些解决方案)与至少一个已发布的解决方案有点类似。它不引用任何表,并且返回最多1048576个值(2 ^ 20)的未排序范围,并且可以根据需要包含负数。您当然可以根据需要对结果进行排序。它运行非常快,特别是在较小范围内。

Select value from dbo.intRange(-500, 1500) order by value  -- returns 2001 values

create function dbo.intRange 
(   
    @Starting as int,
    @Ending as int
)
returns table
as
return (
    select value
    from (
        select @Starting +
            ( bit00.v | bit01.v | bit02.v | bit03.v
            | bit04.v | bit05.v | bit06.v | bit07.v
            | bit08.v | bit09.v | bit10.v | bit11.v
            | bit12.v | bit13.v | bit14.v | bit15.v
            | bit16.v | bit17.v | bit18.v | bit19.v
            ) as value
        from       (select 0 as v union ALL select 0x00001 as v) as bit00
        cross join (select 0 as v union ALL select 0x00002 as v) as bit01
        cross join (select 0 as v union ALL select 0x00004 as v) as bit02
        cross join (select 0 as v union ALL select 0x00008 as v) as bit03
        cross join (select 0 as v union ALL select 0x00010 as v) as bit04
        cross join (select 0 as v union ALL select 0x00020 as v) as bit05
        cross join (select 0 as v union ALL select 0x00040 as v) as bit06
        cross join (select 0 as v union ALL select 0x00080 as v) as bit07
        cross join (select 0 as v union ALL select 0x00100 as v) as bit08
        cross join (select 0 as v union ALL select 0x00200 as v) as bit09
        cross join (select 0 as v union ALL select 0x00400 as v) as bit10
        cross join (select 0 as v union ALL select 0x00800 as v) as bit11
        cross join (select 0 as v union ALL select 0x01000 as v) as bit12
        cross join (select 0 as v union ALL select 0x02000 as v) as bit13
        cross join (select 0 as v union ALL select 0x04000 as v) as bit14
        cross join (select 0 as v union ALL select 0x08000 as v) as bit15
        cross join (select 0 as v union ALL select 0x10000 as v) as bit16
        cross join (select 0 as v union ALL select 0x20000 as v) as bit17
        cross join (select 0 as v union ALL select 0x40000 as v) as bit18
        cross join (select 0 as v union ALL select 0x80000 as v) as bit19
    ) intList
    where @Ending - @Starting < 0x100000
        and intList.value between @Starting and @Ending
)

1
;WITH u AS (
    SELECT Unit FROM (VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) v(Unit)
),
d AS (
    SELECT 
        (Thousands+Hundreds+Tens+Units) V
    FROM 
           (SELECT Thousands = Unit * 1000 FROM u) Thousands 
           ,(SELECT Hundreds = Unit * 100 FROM u) Hundreds 
           ,(SELECT Tens = Unit * 10 FROM u) Tens 
           ,(SELECT Units = Unit FROM u) Units
    WHERE
           (Thousands+Hundreds+Tens+Units) <= 10000
)

SELECT * FROM d ORDER BY v

1

阅读此线程后,我做了以下功能。简单快捷:

go
create function numbers(@begin int, @len int)
returns table as return
with d as (
    select 1 v from (values(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) d(v)
)
select top (@len) @begin -1 + row_number() over(order by (select null)) v
from d d0
cross join d d1
cross join d d2
cross join d d3
cross join d d4
cross join d d5
cross join d d6
cross join d d7
go

select * from numbers(987654321,500000)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.