Como gerar um intervalo de números entre dois números?

141

Eu tenho dois números como entrada do usuário, como por exemplo 1000e 1050.

Como faço para gerar os números entre esses dois números, usando uma consulta sql, em linhas separadas? Eu quero isso:

 1000
 1001
 1002
 1003
 .
 .
 1050
user3211705
fonte

Respostas:

159

Selecione valores não persistentes com a VALUESpalavra - chave. Em seguida, use JOINs para gerar muitas combinações (pode ser estendido para criar centenas de milhares de linhas e além).

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

Uma alternativa mais curta, que não é tão fácil de entender:

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

slartidan
fonte
13
Esta é uma solução fantasticamente elegante #
Aaron Hudon
9
Você pode explicar a sintaxe? Qual é o v (n)?
Rafi
2
@Rafi o v (n) e centenas (n), etc, são de tabelas e colunas nomes / pseudónimos
Twon-ha
106

uma solução alternativa é CTE recursiva:

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)
Jayvee
fonte
4
Não tente usar a opção maxrecusion em uma definição de exibição. Em vez disso, você deve SELECT * FROM CTE_VIEW OPTION (MAXRECURSION 10000) - problemático, se o aplicativo cliente desejar consumir a visualização como ela é.
TvdH 11/11/2015
4
Há uma maxrecursion máxima definida como 32767 (no SQL Server 2012).
BProv
4
Só para esclarecer, se você precisar de uma recursão de mais de 32767, então ele pode ser definido como 0, que significa NOMAX,
Jayvee
2
Aqui está uma demonstração para esta resposta.
stomy
7
Comparei essa resposta com as outras e o plano de execução mostra que essa resposta ( tem o menor custo de consulta e ) é a mais rápida.
Stom
39
SELECT DISTINCT n = number 
FROM master..[spt_values] 
WHERE number BETWEEN @start AND @end

Demo

Observe que esta tabela tem no máximo 2048 porque os números têm lacunas.

Aqui está uma abordagem um pouco melhor usando uma exibição do sistema (desde o 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

ou use uma tabela numérica personalizada. Créditos a Aaron Bertrand, sugiro ler o artigo inteiro: Gere um conjunto ou sequência sem loops

Tim Schmelter
fonte
2
@ user3211705: observe minha edição, esta tabela tem no máximo 2048. Sugiro que leia o artigo inteiro.
Tim Schmelter
3
Eu acho que você pode adicionar WHERE type = 'P'e evitarSELECT DISTINCT
Salman A
1
Seu primeiro link "Demo" continua me dizendoString index out of range: 33
slartidan 19/11/2015
1
Você está certo. Mas parece ser um problema com o SqlFiddle. Isso funciona no seu banco de dados?
amigos estão dizendo sobre tim schmelter
4
Nota rápida, consultas entre bancos de dados como esta não trabalhar com SQL Azure
Kieren Johnstone
33

Recentemente, escrevi esta função com valor de tabela embutida para resolver esse mesmo problema. Seu alcance não é limitado, exceto memória e armazenamento. Ele não acessa tabelas, portanto não há necessidade de leituras ou gravações de disco em geral. Ele adiciona valores de associações exponencialmente em cada iteração, tornando-o muito rápido, mesmo para intervalos muito grandes. Ele cria dez milhões de registros em cinco segundos no meu servidor. Também funciona com valores negativos.

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

É útil também para períodos e datas:

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

Você pode usar uma junção de aplicação cruzada nela para dividir registros com base nos valores na tabela. Por exemplo, para criar um registro para cada minuto em um intervalo de tempo em uma tabela, você pode fazer algo como:

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
Brian Pressler
fonte
1
Uau, essa consulta inicial é RÁPIDA. Muito mais rápido que a solução CLR postada acima. Obrigado!
precisa saber é o seguinte
1
Bom - ainda tenho um cliente no SQL Server 2008 e foi exatamente isso que eu precisava! Muito esperto!
STLDev
1
funciona para 1-100, mas depois falha. Mesmo o seu exemplo de geração de 5-500 não funciona para mim, mostra 5, 21, ... 484, 500
Rez.Net
3
Se você deseja classificá-lo, terá que adicionar um pedido por cláusula: #SELECT X FROM fn_ConsecutiveNumbers(5, 500) ORDER BY X;
Brian Pressler
29

A melhor opção que usei é a seguinte:

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

Eu gerei milhões de registros usando isso e funciona perfeitamente.

Habeeb
fonte
2
Essa é a solução mais elegante aqui, mas acho que é difícil para muitas pessoas entenderem (eu estava fazendo isso com master.sys.all_columns). @ STLDeveloper, sim, funciona com 2008 e posteriores.
Cetin Basoz 28/01/19
13

Isso funciona para mim!

select top 50 ROW_NUMBER() over(order by a.name) + 1000 as Rcount
from sys.all_objects a
Nguyen Son Tung
fonte
2
Nice one-liner - mas lembre-se de que o número máximo de linhas dependerá sys.all_objects- para pequenos intervalos <2000 itens, isso não é um problema. Não tem certeza se ele terá problemas de permissões? perfeito para gerar rapidamente um lote de dados de teste.
freedomn-m
@ freedomn-m Uma maneira de aumentar o número máximo de linhas seria executar uma junção auto-cruzada. select top 50 ROW_NUMBER() over(order by a.name) + 1000 as Rcount from sys.all_objects a, sys.all_objects b. Onde eu só podia gerar 2384 linhas antes, agora posso gerar 5683456 linhas.
Klicker
9

A melhor maneira é usar ctes recursivas.

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)

saludos.

cesargroening
fonte
1
Isso foi muito útil. Modifiquei o código para inserir 100.000 linhas. Com a minha solução, demorou 13 minutos; usando o seu, levou cinco segundos. Muchísimas gracias.
Cthulhu
2
Na verdade, CTEs recursivas são uma das piores maneiras de contar. Eles podem até ser vencidos por um loop While em uma transação e o While Loop produzirá muito menos leituras. O método cCTE (CTEs em cascata, originalmente de Itizik Ben-Gan) é muito mais rápido e produz zero leituras.
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
Sowbarani Karthikeyan
fonte
1
Isso é diferente da resposta de @Jayvee?
Noel
1
Sim, em que condição é mencionado como num + 1 <1050, que imprimirá até 1049 apenas.
Sowbarani Karthikeyan
2
Uma edição (ou comentário) da resposta existente essencial é o mesmo que forneceria mais valor do que uma resposta totalmente nova.
Noel
7

Se você não tiver problemas para instalar um assembly CLR no servidor, uma boa opção é escrever uma função com valor de tabela no .NET. Dessa forma, você pode usar uma sintaxe simples, facilitando a associação com outras consultas e, como bônus, não desperdiçará memória porque o resultado é transmitido.

Crie um projeto contendo a seguinte classe:

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() { }
    }
}

Coloque o assembly em algum lugar no servidor e execute:

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

Agora você pode executar:

select * from dbo.seq(1, 1000000)
AlexDev
fonte
1
Eu tentei esta solução e funciona bem, mas não super rápido. Se você está gerando apenas 1.000 números, ou talvez 10.000, é bastante rápido. Se você é como eu e precisa gerar bilhões de números, a solução de Brian Pressler abaixo é incrivelmente rápida em comparação com o SQL CLR.
Derreck Dean
2
@DerreckDean Você está certo. Eu acho que a dele é a melhor solução, porque é fácil de criar e usar (e rápido como você diz). No meu caso, eu já tinha uma montagem para concatenar seqüências de caracteres, então apenas a adicionei lá.
AlexDev 28/09/16
1
Eu também tinha uma montagem e tentei os dois métodos. Estou gerando um número indeterminado de números para adicionar às datas (basicamente, recriei o agendador do agente do SQL Server para gerar datas para nosso aplicativo interno, e 100 níveis de recursão não seriam suficientes para gerar vários anos de às vezes, possivelmente até a segunda.), então pude testar minuciosamente várias soluções desse segmento. Agradeço sua contribuição!
Derreck Dean
7

Nada de novo, mas eu reescrevi a solução Brian Pressler para ficar mais fácil, pode ser útil para alguém (mesmo que seja apenas para mim):

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
Guillaume86
fonte
1
Acredito que você tenha destilado a essência de um belo algoritmo em algum código absolutamente bonito.
Clay
1
Os resultados são ordenados em uma ordem estranha, mas não caótica. Teste-o na faixa de 5 a 500. Retorna 5,21,37, ..., 245,6,22, ... Você sabe como a ordem influenciaria o desempenho? Soluções baseadas em ROW_NUMBER()não têm esse problema.
Przemyslaw Remin
1
Não sou especialista, mas, intuitivamente, acho que o servidor SQL precisará colocar todos os resultados na memória e ordená-los antes de devolvê-los para maior uso da memória e resposta atrasada, em vez de apenas transmitir os resultados à medida que eles chegarem.
Guillaume86
6

Dois anos depois, mas descobri que tinha o mesmo problema. Aqui está como eu resolvi isso. (editado para incluir parâmetros)

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)
besouro
fonte
5

a resposta de slartidan pode ser melhorada, em termos de desempenho, eliminando todas as referências ao produto cartesiano e usando-o ROW_NUMBER()(em comparação com o plano de execução ):

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

Coloque-o dentro de um CTE e adicione uma cláusula where para selecionar os números desejados:

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
Salman A
fonte
1
ROW_NUMBER começa apenas em 1. Como podemos começar do zero com seu método?
Stom
2
@stomy SELECT ROW_NUMBER() OVER (...) - 1 AS n. Em alguns casos, isso pode prejudicar o desempenho.
Salman A
4

Aqui estão algumas soluções ótimas e compatíveis:

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
;
jumxozizi
fonte
1
Este método é de alguma forma melhor do que simplesmente selecting where spt_values.number between @min and @max?
underscore_d
2
O filtro Type = 'P' é necessário para evitar números duplicados. Com esse filtro, a tabela retornará os números de 0 a 2047. Portanto, o filtro "número entre @min e @max" funcionará enquanto as variáveis ​​estiverem dentro desse intervalo. Minha solução permitirá obter até 2048 linhas dentro do intervalo inteiro (-2.147.483.648) - (2.147.483.647).
precisa saber é o seguinte
1
a lógica acima é útil apenas quando a diferença entre o número mínimo de menos do que 2048 e máxima e uma vez que pode, no máximo de 2048 registos em um ponto de tempo
Smart003
4

Sei que estou quatro anos atrasado, mas me deparei com outra resposta alternativa para esse problema. O problema da velocidade não é apenas a pré-filtragem, mas também a prevenção da classificação. É possível forçar a ordem de junção para executar da maneira que o produto cartesiano realmente conta como resultado da junção. Usando a resposta de slartidan como ponto de partida:

    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

Se soubermos o intervalo que queremos, podemos especificá-lo via @Upper e @Lower. Ao combinar a dica de junção REMOTE e TOP, podemos calcular apenas o subconjunto de valores que queremos sem desperdiçar nada.

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

A dica de junção REMOTE força o otimizador a comparar primeiro o lado direito da junção. Ao especificar cada junção como REMOTE do valor mais ao menos significativo, a junção em si contará mais uma vez corretamente. Não há necessidade de filtrar com um WHERE ou classificar com um ORDER BY.

Se você deseja aumentar o intervalo, pode continuar a adicionar junções adicionais com ordens de magnitude progressivamente mais altas, desde que ordenadas da mais para a menos significativa na cláusula FROM.

Observe que esta é uma consulta específica para o SQL Server 2008 ou superior.

mecóide
fonte
1
Muito bom mesmo. A mesma técnica pode ser aplicada à excelente resposta de Brian Pressler e à adorável reescrita de Guillaume86.
Clay
3

Isso também fará

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
BICube
fonte
3

A melhor velocidade ao executar a consulta

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

CTE recursivo em tamanho exponencial (mesmo com o padrão de 100 recursões, isso pode gerar até 2 ^ 100 números):

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
user7555577
fonte
De acordo com o OP, penso @startnume endnumdeve ser inserido pelo usuário?
JC
2

Eu tive que inserir o caminho do arquivo de imagem no banco de dados usando um método semelhante. A consulta abaixo funcionou bem:

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

O código para você seria:

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

    SET @num = @num + 1
end
Maludasek
fonte
2

É isso que eu faço, é bem rápido e flexível e não tem muito código.

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;

Observe que (ORDER BY @count) é um manequim. Não faz nada, mas ROW_NUMBER () requer um ORDER BY.

Edit : eu percebi que a pergunta original era obter um intervalo de x até y. Meu script pode ser modificado assim para obter um intervalo:

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;
JohnnyIV
fonte
1
Isso foi muito rápido - e flexível. Funcionou bem para as minhas necessidades.
AndrewBanjo1968 24/03
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
Elexsandro Rangel dos Santos
fonte
1

Isso funciona apenas para seqüências, desde que algumas tabelas de aplicativos tenham linhas. Suponha que eu queira a sequência de 1..100 e tenha a tabela de aplicativos dbo.foo com a coluna (do tipo numérico ou de seqüência de caracteres) foo.bar:

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

Apesar de estar presente em uma cláusula order by, o dbo.foo.bar não precisa ter valores distintos ou mesmo nulos.

Obviamente, o SQL Server 2012 tem objetos de sequência, então há uma solução natural nesse produto.

BobHy
fonte
1

Aqui está o que eu vim com:

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

Gera até 2 ^ 24 valores. As condições de junção mantêm-no rápido para valores pequenos.

τεκ
fonte
1

Isso foi concluído em 36 segundos em nosso servidor DEV. Como a resposta de Brian, o foco na filtragem para o intervalo é importante na consulta; a BETWEEN ainda tenta gerar todos os registros iniciais antes do limite inferior, mesmo que não precise deles.

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

Note-se que ROW_NUMBER é um bigint , por isso não podemos passar por cima de 2 ^^ 64 (== 16 ^^ 16) registros gerados com qualquer método que usa-lo. Esta consulta, portanto, respeita o mesmo limite superior nos valores gerados.

J Bryan Price
fonte
1

Isso usa código processual e uma função com valor de tabela. Lento, mas fácil e previsível.

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

Uso:

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

É uma tabela, para que você possa usá-lo em junções com outros dados. Eu frequentemente uso essa função como o lado esquerdo de uma junção contra um GROUP BY hora, dia etc. para garantir uma sequência contígua de valores de tempo.

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

O desempenho não é inspirador (16 segundos para um milhão de linhas), mas é bom o suficiente para muitos propósitos.

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

Oracle 12c; Rápido, mas limitado:

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

Nota : limitado à contagem de linhas da visualização all_objects;

Witold Kaczurba
fonte
1

A solução que eu desenvolvi e usei há algum tempo (usando alguns dos trabalhos compartilhados de outros) é um pouco semelhante a pelo menos um postado. Ele não faz referência a nenhuma tabela e retorna um intervalo não classificado de até 1048576 valores (2 ^ 20) e pode incluir negativos, se desejado. Obviamente, você pode classificar o resultado, se necessário. Ele roda muito rapidamente, especialmente em faixas menores.

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
)
rlhane
fonte
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
Chriz
fonte
1

Eu fiz a função abaixo depois de ler este tópico. Simples e rápido:

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)
Lanjing Jin
fonte