How to generate a range of numbers between two num

2019-01-01 03:43发布

I have two numbers as input from the user, like for example 1000 and 1050.

How do I generate the numbers between these two numbers, using a sql query, in seperate rows? I want this:

 1000
 1001
 1002
 1003
 .
 .
 1050

24条回答
永恒的永恒
2楼-- · 2019-01-01 04:36

This uses procedural code and a table-valued function. Slow, but easy and predictable.

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

Usage:

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

It's a table, so you can use it in joins with other data. I most frequently use this function as the left side of a join against a GROUP BY hour, day etc to ensure a contiguous sequence of time values.

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
(...)

Performance is uninspiring (16 seconds for a million rows) but good enough for many purposes.

SELECT count(1) FROM [dbo].[Sequence] (
   1000001
  ,2000000)
GO
查看更多
泛滥B
3楼-- · 2019-01-01 04:37

Here are couple quite optimal and compatible solutions:

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
;
查看更多
忆尘夕之涩
4楼-- · 2019-01-01 04:38

I know I'm 4 years too late, but I stumbled upon yet another alternative answer to this problem. The issue for speed isn't just pre-filtering, but also preventing sorting. It's possible to force the join-order to execute in a manner that the Cartesian product actually counts up as a result of the join. Using slartidan's answer as a jump-off point:

    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

If we know the range we want, we can specify it via @Upper and @Lower. By combining the join hint REMOTE along with TOP, we can calculate only the subset of values we want with nothing wasted.

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

The join hint REMOTE forces the optimizer to compare on the right side of the join first. By specifying each join as REMOTE from most to least significant value, the join itself will count upwards by one correctly. No need to filter with a WHERE, or sort with an ORDER BY.

If you want to increase the range, you can continue to add additional joins with progressively higher orders of magnitude, so long as they're ordered from most to least significant in the FROM clause.

Note that this is a query specific to SQL Server 2008 or higher.

查看更多
浅入江南
5楼-- · 2019-01-01 04:40

The best option I have used is as follows:

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

I have generated millions of records using this and it works perfect.

查看更多
看风景的人
6楼-- · 2019-01-01 04:42

I recently wrote this inline table valued function to solve this very problem. It's not limited in range other than memory and storage. It accesses no tables so there's no need for disk reads or writes generally. It adds joins values exponentially on each iteration so it's very fast even for very large ranges. It creates ten million records in five seconds on my server. It also works with negative values.

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);

It's handy for date and time ranges as well:

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'));

You could use a cross apply join on it to split records based on values in the table. So for example to create a record for every minute on a time range in a table you could do something like:

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
查看更多
浪荡孟婆
7楼-- · 2019-01-01 04:42

Oracle 12c; Quick but limited:

select rownum+1000 from all_objects fetch first 50 rows only;

Note: limited to row count of all_objects view;

查看更多
登录 后发表回答