web-dev-qa-db-fra.com

Comment générer une plage de nombres entre deux nombres?

J'ai deux chiffres en entrée de l'utilisateur, comme par exemple 1000 et 1050.

Comment générer les nombres entre ces deux nombres, en utilisant une requête SQL, dans des lignes séparées? Je veux ceci:

 1000
 1001
 1002
 1003
 .
 .
 1050
110
user3211705

Sélectionnez des valeurs non persistantes à l'aide du mot clé VALUES. Puis utilisez JOINs pour générer beaucoup de combinaisons (peut être étendu pour créer des centaines de milliers de lignes et au-delà).

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

Une alternative plus courte, qui n'est pas aussi facile à comprendre:

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

125
slartidan

une solution alternative est le CTE récursif:

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)
83
Jayvee
SELECT DISTINCT n = number 
FROM master..[spt_values] 
WHERE number BETWEEN @start AND @end

Demo

Notez que ce tableau a un maximum de 2048 car alors les nombres ont des trous.

Voici une approche légèrement meilleure utilisant une vue système (à partir de 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 utilisez une table de nombres personnalisée. Crédits à Aaron Bertrand, je suggère de lire l'article complet: Générer un ensemble ou une séquence sans boucles

29
Tim Schmelter

J'ai récemment écrit cette fonction de valeur de table en ligne pour résoudre ce problème même. Sa portée ne se limite pas à la mémoire et au stockage. Il n’accède à aucune table et n’a donc généralement pas besoin d’être lu ou écrit sur le disque. Il ajoute des valeurs de jointure de manière exponentielle à chaque itération, ce qui le rend très rapide, même pour de très grandes plages. Il crée dix millions d’enregistrements en cinq secondes sur mon serveur. Cela fonctionne aussi avec des valeurs négatives.

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

C'est pratique pour les plages de dates et d'heures:

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

Vous pouvez utiliser une jointure à application croisée pour fractionner les enregistrements en fonction des valeurs de la table. Ainsi, par exemple, pour créer un enregistrement pour chaque minute sur une plage de temps dans un tableau, vous pouvez faire quelque chose comme:

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
26
Brian Pressler

La meilleure option que j'ai utilisée est la suivante:

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

J'ai généré des millions de disques en utilisant cela et cela fonctionne parfaitement.

21
Habeeb

Ça marche pour moi!

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

Si vous n’avez pas de problème à installer un assemblage CLR sur votre serveur, une bonne option est d’écrire une fonction table dans .NET. De cette façon, vous pouvez utiliser une syntaxe simple, ce qui facilite la jonction avec d’autres requêtes et, en prime, ne gaspille pas de mémoire car le résultat est diffusé en continu.

Créez un projet contenant la classe suivante:

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

Placez l'assemblage quelque part sur le serveur et exécutez:

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

Maintenant vous pouvez lancer:

select * from dbo.seq(1, 1000000)
6
AlexDev

Le meilleur moyen est d'utiliser des ctes récursifs.

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.

6
cesargroening

Rien de nouveau mais j'ai récrit la solution de Brian Pressler pour être plus facile à regarder, cela pourrait être utile à quelqu'un (même si ce n'est que moi à l'avenir):

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
6
Guillaume86

2 ans plus tard, mais j'ai constaté que j'avais le même problème. Voici comment je l'ai résolu. (édité pour inclure des paramètres)

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
beetle
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
4

Je sais que j'ai 4 ans de retard, mais je suis tombé sur une autre solution de rechange à ce problème. Le problème de la vitesse ne concerne pas seulement le préfiltrage, il empêche également le tri. Il est possible de forcer l'ordre de jointure à s'exécuter de manière à ce que le produit cartésien compte réellement à la suite de la jointure. En utilisant la réponse de slartidan comme point de départ:

    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

Si nous connaissons la plage souhaitée, nous pouvons le spécifier via @Upper et @Lower. En combinant l'indice de jointure REMOTE avec TOP, nous pouvons calculer uniquement le sous-ensemble de valeurs que nous voulons sans rien perdre.

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

L'indicateur de jointure REMOTE oblige l'optimiseur à comparer d'abord sur le côté droit de la jointure. En spécifiant chaque jointure comme REMOTE du plus significatif au moins significatif, la jointure elle-même comptera correctement d'une unité vers le haut. Pas besoin de filtrer avec WHERE ou de trier avec ORDER BY.

Si vous souhaitez augmenter la plage, vous pouvez continuer à ajouter des jointures avec des ordres de grandeur progressivement croissants, à condition qu'elles soient classées du plus important au moins significatif dans la clause FROM.

Notez qu'il s'agit d'une requête spécifique à SQL Server 2008 ou version ultérieure.

3
mechoid

la réponse de slartidan peut être améliorée, en termes de performances, en éliminant toutes les références au produit cartésien et en utilisant ROW_NUMBER() à la place ( plan d'exécution comparé ):

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

Enroulez-le dans un CTE et ajoutez une clause where pour sélectionner les numéros souhaités:

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
3
Salman A

Voici quelques solutions assez optimales et compatibles:

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
;
3
jumxozizi

La meilleure vitesse lors de l'exécution de la requête

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

cTE récursif de taille exponentielle (même par défaut de 100 récursions, cela peut construire jusqu'à 2 ^ 100 nombres):

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
2
user7555577

Cela fera aussi

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
2
BICube

J'ai dû insérer le chemin de fichier de l'image dans la base de données en utilisant une méthode similaire. La requête ci-dessous a bien fonctionné:

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

Le code pour vous serait:

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

    SET @num = @num + 1
end
1
Maludasek

Oracle 12c; Rapide mais limité:

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

Note: limité au nombre de lignes de la vue all_objects;

0
Witold Kaczurba

C'est ce que je fais, c'est assez rapide et flexible et pas beaucoup de code.

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;

Notez que (ORDER BY @count) est un mannequin. Il ne fait rien mais ROW_NUMBER () nécessite un ORDER BY.

Edit: Je me suis rendu compte que la question initiale était d'obtenir une plage de x à y. Mon script peut être modifié comme ceci pour obtenir une plage:

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;
0
JohnnyIV

Cela ne fonctionne que pour les séquences tant que certaines tables d'application ont des lignes. Supposons que je veuille une séquence à partir de 1..100 et que la table d’application dbo.foo soit associée à une colonne (de type numérique ou chaîne) foo.bar:

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

Malgré sa présence dans une clause order by, dbo.foo.bar ne doit pas nécessairement comporter de valeurs distinctes, ni même non nulles.

Bien sûr, SQL Server 2012 a des objets séquence, ce qui en fait une solution naturelle.

0
BobHy

Voici ce que je suis venu avec:

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

Génère jusqu'à 2 ^ 24 valeurs. Les conditions de participation le maintiennent rapidement pour les petites valeurs.

0
τεκ

Ceci s’est terminé en 36 secondes sur notre serveur DEV. À l'instar de la réponse de Brian, il est important de se concentrer sur le filtrage en fonction de la plage. BETWEEN essaie toujours de générer tous les enregistrements initiaux avant la limite inférieure, même s’il n’en a pas besoin.

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

Notez que ROW_NUMBER est un bigint, nous ne pouvons donc pas dépasser 2 ^^ 64 (== 16 ^^ 16) enregistrements générés avec une méthode qui l'utilise. Cette requête respecte donc la même limite supérieure pour les valeurs générées.

0
J Bryan Price

Ceci utilise un code de procédure et une fonction table. Lent, mais facile et prévisible.

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

C'est une table, vous pouvez donc l'utiliser dans des jointures avec d'autres données. J'utilise le plus souvent cette fonction comme le côté gauche d'une jointure par rapport à une heure, un jour, un groupe, etc. afin de garantir une séquence contiguë de valeurs temporelles.

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

Les performances sont médiocres (16 secondes pour un million de lignes) mais suffisent à de nombreuses fins.

SELECT count(1) FROM [dbo].[Sequence] (
   1000001
  ,2000000)
GO
0
Robert Calhoun
-- 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

La solution que je développe et utilise depuis quelque temps déjà (chevaucher certaines œuvres sur des œuvres partagées par d'autres) est légèrement similaire à au moins une mise en ligne. Il ne référence aucune table et renvoie une plage non triée allant jusqu'à 1048576 valeurs (2 ^ 20) et peut inclure des négatifs si vous le souhaitez. Vous pouvez bien sûr trier le résultat si nécessaire. Il fonctionne assez rapidement, surtout sur les petites gammes.

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
)
0
rlhane