How to generate a range of numbers between two numbers?

后端 未结 30 2258
执念已碎
执念已碎 2020-11-22 10:16

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

30条回答
  •  醉梦人生
    2020-11-22 10:31

    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
    

提交回复
热议问题