두 숫자 사이의 숫자 범위를 생성하는 방법은 무엇입니까?


141

사용자의 입력으로 두 개의 숫자가 있습니다 (예 : 1000및) 1050.

별도의 행에서 SQL 쿼리를 사용 하여이 두 숫자 사이의 숫자를 어떻게 생성합니까? 나는 이것을 원한다 :

 1000
 1001
 1002
 1003
 .
 .
 1050

답변:


159

VALUES키워드를 사용하여 비 지속 값을 선택하십시오 . 그런 다음 JOINs를 사용 하여 많은 조합을 생성하십시오 (수십만 행 이상으로 확장 가능).

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
이 환상적으로 우아한 솔루션입니다
아론 Hudon

9
구문을 설명 할 수 있습니까? v (n)은 무엇입니까?
Rafi

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)-문제가 있습니다.
TvdH

4
최대 maxrecursion 은 32767 (SQL Server 2012)로 설정되어 있습니다.
BProv

4
명확히하기 위해 32767 이상의 재귀가 필요한 경우 nomax를 의미하는 0으로 설정할 수 있습니다.
Jayvee

2
이 답변에 대한 데모 는 다음과 같습니다 .
Stomy

7
이 답변을 다른 답변과 비교했으며 실행 계획에 따르면이 답변 ( 최소 쿼리 비용이 있으며 )이 가장 빠릅니다.
stomy

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입니다. 전체 기사를 읽는 것이 좋습니다.
Tim Schmelter

3
나는 당신이 추가 하고 피할 수 있다고 생각 합니다WHERE type = 'P'SELECT DISTINCT
Salman A

1
첫 번째 "데모"링크가 계속 알려줍니다String index out of range: 33
slartidan

1
네가 옳아. 그러나 SqlFiddle에는 문제가있는 것 같습니다. DB에서 작동합니까?
팀 슈멜 터

4
참고로 이와 같은 데이터베이스 간 쿼리는 SQL Azure에서 작동하지 않습니다.
Kieren Johnstone

33

최근 에이 인라인 테이블 값 함수를 작성 하여이 문제를 해결했습니다. 메모리 및 스토리지 이외의 범위에는 제한이 없습니다. 테이블에 액세스하지 않으므로 일반적으로 디스크 읽기 또는 쓰기가 필요하지 않습니다. 각 반복마다 지수 적으로 결합 값을 추가하므로 매우 넓은 범위에서도 매우 빠릅니다. 내 서버에서 5 초 안에 천만 개의 레코드를 만듭니다. 음수 값으로도 작동합니다.

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

교차 적용 조인을 사용하여 테이블의 값을 기반으로 레코드를 분할 할 수 있습니다. 예를 들어 테이블의 시간 범위에서 1 분마다 레코드를 작성하려면 다음과 같이 할 수 있습니다.

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

1
니스-나는 여전히 SQL Server 2008에 클라이언트를 가지고 있으며 이것은 내가 필요한 것입니다! 매우 영리한!
STLDev

1
1-100에서 작동하지만 실패합니다. 5-500을 생성하는 당신의 예조차도 나를 위해 작동하지 않습니다, 그것은 5, 21, ... 484, 500을 보여줍니다
Rez.Net

3
당신이 그것을 정렬하려면 당신은 절에 의해 주문을 추가해야합니다 :SELECT X FROM fn_ConsecutiveNumbers(5, 500) ORDER BY X;
브라이언 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 개 미만의 작은 범위의 경우 문제가되지 않습니다. 권한 문제가 있는지 확실하지 않습니까? 일련의 테스트 데이터를 빠르게 생성하는 데 적합합니다.
freedomn-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 분이 걸렸습니다. 당신을 사용하면 5 초가 걸렸습니다. Muchísimas gracias.
크 툴후

2
실제로 재귀 CTE는 계산하는 가장 나쁜 방법 중 하나입니다. 트랜잭션에서 While 루프로 이길 수도 있고 While 루프는 훨씬 적은 판독 값을 생성합니다. cCTE 방법 (원래 Itizik Ben-Gan에 의한 Cascading CTE)은 훨씬 빠르며 제로 판독을 생성합니다.
Jeff Moden

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

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 개의 숫자 만 생성한다면 상당히 빠릅니다. 나와 같이 수십억 개의 숫자를 생성 해야하는 경우 아래의 Brian Pressler 솔루션은 SQL CLR에 비해 믿을 수 없을 정도로 빠릅니다.
Derreck Dean

2
@DerreckDean 당신이 맞아요. 나는 작성하고 사용하기 쉽고 (당신이 말한대로 빠르기 때문에) 그의 최선의 해결책이라고 생각합니다. 내 경우에는 이미 문자열을 연결하기위한 어셈블리가 있었으므로 방금 추가했습니다.
AlexDev

1
기존 어셈블리도 가지고 있었고 두 가지 방법을 모두 시도했습니다. 날짜에 추가 할 불확실한 수의 숫자를 생성하고 있습니다 (기본적으로 사내 응용 프로그램의 날짜를 생성하기 위해 SQL Server 에이전트 스케줄러를 다시 작성했으며 100 년의 재귀는 여러 해 동안 생성되지 않았습니다. 날짜 시간 (아마도 초까지 가능).) 그래서이 스레드에서 여러 솔루션을 철저히 테스트 할 수있었습니다. 당신의 기여에 감사드립니다!
Derreck Dean

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
나는 당신이 아름다운 알고리즘의 본질을 아주 예쁜 코드로 증류했다고 생각합니다.
Clay

1
결과는 이상하지만 혼란스러운 순서로 정렬됩니다. 5에서 500 사이의 범위에서 테스트하십시오. 5,21,37, ..., 245,6,22를 반환합니다. 주문이 성능에 어떤 영향을 미치는지 알고 있습니까? 기반 솔루션 ROW_NUMBER()에는 해당 문제가 없습니다.
Przemyslaw Remin

1
나는 전문가는 아니지만 직관적으로 SQL 서버는 결과를 스트리밍하는 것이 아니라 더 많은 메모리 사용과 지연 된 응답을 반환하기 전에 모든 결과를 메모리에 저장하고 순서를 정해야한다고 생각합니다.

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

slartidan의 답변 은 직교 제품에 대한 모든 참조를 제거하고 ROW_NUMBER()대신 ( 실행 계획 비교 ) 사용하여 성능 측면에서 개선 될 수 있습니다 .

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부터 시작합니다. 방법을 어떻게 0부터 시작할 수 있습니까?
stomy

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
이 방법은 단순히 selecting 보다 어떻게 낫 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의 멋진 재 작성에도 동일한 기술을 적용 할 수 있습니다.
Clay

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

쿼리를 실행할 때 최고 속도

DECLARE @num INT = 1000
WHILE(@num<1050)
begin
 INSERT  INTO [dbo].[Codes]
    (   Code
    ) 
    VALUES (@num)
    SET @num = @num + 1
end

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

영업에 따르면, 내 생각 @startnumendnum사용자가 입력을해야 하는가?
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

order by 절에 존재하더라도 dbo.foo.bar는 고유하거나 널이 아닌 값을 가질 필요는 없습니다.

물론 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_NUMBER는 A는 BIGINT 사용을하는 것이 우리가 어떤 방법으로 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

오라클 12c; 빠르지 만 제한적 :

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

참고 : all_objects보기의 행 수로 제한됩니다.


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)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.