J'ai deux nombres comme entrée de l'utilisateur, comme par exemple 1000
et 1050
.
Comment générer les nombres entre ces deux nombres, à l'aide d'une requête SQL, en lignes séparées? Je veux ceci:
1000
1001
1002
1003
.
.
1050
J'ai deux nombres comme entrée de l'utilisateur, comme par exemple 1000
et 1050
.
Comment générer les nombres entre ces deux nombres, à l'aide d'une requête SQL, en lignes séparées? Je veux ceci:
1000
1001
1002
1003
.
.
1050
Réponses:
Sélectionnez des valeurs non persistantes avec le VALUES
mot clé. Ensuite, utilisez JOIN
s pour générer beaucoup et 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
Une alternative plus courte, qui n'est pas aussi simple à 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
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)
SELECT DISTINCT n = number
FROM master..[spt_values]
WHERE number BETWEEN @start AND @end
Notez que ce tableau a un maximum de 2048 car alors les nombres ont des lacunes.
Voici une approche légèrement meilleure utilisant une vue système (depuis 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;
ou utilisez une table numérique personnalisée. Crédits à Aaron Bertrand, je propose de lire l'article en entier: Générer un ensemble ou une séquence sans boucles
WHERE type = 'P'
et éviterSELECT DISTINCT
String index out of range: 33
J'ai récemment écrit cette fonction de table en ligne pour résoudre ce problème. Il n'est pas limité dans la plage autre que la mémoire et le stockage. Il n'accède à aucune table, il n'est donc pas nécessaire de lire ou d'écrire sur le disque en général. 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 également 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 également 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 diviser 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 une table, 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
SELECT X FROM fn_ConsecutiveNumbers(5, 500) ORDER BY X;
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 d'enregistrements en utilisant cela et cela fonctionne parfaitement.
Ça marche pour moi!
select top 50 ROW_NUMBER() over(order by a.name) + 1000 as Rcount
from sys.all_objects a
sys.all_objects
- pour les petites plages <2000 éléments, ce n'est pas un problème. Vous ne savez pas s'il aura des problèmes d'autorisations? parfait pour générer rapidement un lot de données de test.
select top 50 ROW_NUMBER() over(order by a.name) + 1000 as Rcount from sys.all_objects a, sys.all_objects b
. Là où je ne pouvais générer que 2384 lignes auparavant, je peux maintenant générer 5683456 lignes.
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.
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
Si vous ne rencontrez pas de problème pour installer un assembly CLR sur votre serveur, une bonne option consiste à é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é.
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];
Vous pouvez maintenant exécuter:
select * from dbo.seq(1, 1000000)
Rien de nouveau mais j'ai réécrit la solution de Brian Pressler pour être plus agréable à l'œil, cela pourrait être utile à quelqu'un (même si c'est juste pour moi):
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
ROW_NUMBER()
n'ont pas ce problème.
2 ans plus tard, mais j'ai découvert que j'avais le même problème. Voici comment je l'ai résolu. (modifié pour inclure les 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)
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 à la ROW_NUMBER()
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
Enveloppez-le dans un CTE et ajoutez une clause where pour sélectionner les nombres 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
SELECT ROW_NUMBER() OVER (...) - 1 AS n
. Dans certains cas, cela peut nuire aux performances.
Voici quelques solutions tout à fait 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
;
select
ingénierie where spt_values.number between @min and @max
?
Je sais que j'ai 4 ans trop tard, mais je suis tombé sur une autre solution alternative à ce problème. Le problème de la vitesse n'est pas seulement le pré-filtrage, mais aussi la prévention du tri. Il est possible de forcer l'ordre de jointure à s'exécuter d'une manière 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 que nous voulons, nous pouvons la spécifier via @Upper et @Lower. En combinant l'indicateur 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'indication de jointure REMOTE force l'optimiseur à comparer en premier sur le côté droit de la jointure. En spécifiant chaque jointure comme REMOTE de la valeur la plus importante à la valeur la moins significative, la jointure elle-même comptera correctement de un. Pas besoin de filtrer avec un WHERE ou de trier avec un ORDER BY.
Si vous souhaitez augmenter la plage, vous pouvez continuer à ajouter des jointures supplémentaires avec des ordres de grandeur progressivement plus élevés, à 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 supérieur.
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
CTE récursif en taille exponentielle (même pour la récursivité par défaut de 100, cela peut générer 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
@startnum
et endnum
devrait être saisi par l'utilisateur?
J'ai dû insérer le chemin du fichier 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
C'est ce que je fais, c'est assez rapide et flexible et peu 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 : J'ai réalisé 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;
-- 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
Cela ne fonctionne que pour les séquences tant que certaines tables d'application ont des lignes. Supposons que je veux une séquence de 1 à 100 et que la table d'application dbo.foo avec la 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 n'a pas besoin d'avoir des valeurs distinctes ou même non nulles.
Bien sûr, SQL Server 2012 a des objets de séquence, il existe donc une solution naturelle dans ce produit.
Voici ce que j'ai trouvé:
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 jointure le maintiennent rapide pour les petites valeurs.
Cela s'est terminé pour moi en 36 secondes sur notre serveur DEV. Comme la réponse de Brian, il est important de se concentrer sur le filtrage de la plage depuis la requête; a 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 , donc nous ne pouvons pas parcourir 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 sur les valeurs générées.
Cela utilise un code procédural 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 en jointure avec d'autres données. J'utilise le plus souvent cette fonction comme côté gauche d'une jointure contre une heure, un jour, etc. GROUP BY pour assurer une séquence contiguë de valeurs de temps.
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 sans intérêt (16 secondes pour un million de lignes) mais suffisantes pour de nombreuses raisons.
SELECT count(1) FROM [dbo].[Sequence] (
1000001
,2000000)
GO
Oracle 12c; Rapide mais limité:
select rownum+1000 from all_objects fetch first 50 rows only;
Remarque : limité au nombre de lignes de la vue all_objects;
La solution que j'ai développée et utilisée depuis un certain temps maintenant (chevauchant certains sur les œuvres partagées d'autres) est légèrement similaire à au moins une publiée. Il ne fait 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, en particulier sur les plus petites plages.
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
)
;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
J'ai créé la fonction ci-dessous après avoir lu ce fil. Simple et rapide:
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)