Mam dwie liczby jako dane wejściowe od użytkownika, jak na przykład 1000
i1050
.
Jak wygenerować liczby między tymi dwiema liczbami, używając zapytania sql, w oddzielnych wierszach? Chcę to:
1000
1001
1002
1003
.
.
1050
Mam dwie liczby jako dane wejściowe od użytkownika, jak na przykład 1000
i1050
.
Jak wygenerować liczby między tymi dwiema liczbami, używając zapytania sql, w oddzielnych wierszach? Chcę to:
1000
1001
1002
1003
.
.
1050
Odpowiedzi:
Wybierz nietrwałe wartości za pomocą VALUES
słowa kluczowego. Następnie użyj JOIN
s, aby wygenerować wiele, wiele kombinacji (można je rozszerzyć, aby utworzyć setki tysięcy wierszy i więcej).
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
Krótsza alternatywa, która nie jest tak łatwa do zrozumienia:
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
alternatywnym rozwiązaniem jest rekurencyjne 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)
SELECT DISTINCT n = number
FROM master..[spt_values]
WHERE number BETWEEN @start AND @end
Zauważ, że ta tabela ma maksymalnie 2048, ponieważ wtedy liczby mają luki.
Oto nieco lepsze podejście przy użyciu widoku systemu (od wersji 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;
lub użyj niestandardowej tabeli liczbowej. Podziękowania dla Aarona Bertranda, proponuję przeczytać cały artykuł: Wygeneruj zestaw lub sekwencję bez pętli
WHERE type = 'P'
i uniknąćSELECT DISTINCT
String index out of range: 33
Niedawno napisałem tę funkcję wartościowaną w tabeli inline, aby rozwiązać ten właśnie problem. Nie jest ograniczony zasięgiem innym niż pamięć i przechowywanie. Nie uzyskuje dostępu do tabel, więc nie ma potrzeby wykonywania odczytów ani zapisów na dysku. Dodaje wartości złączeń wykładniczo przy każdej iteracji, dzięki czemu jest bardzo szybki nawet dla bardzo dużych zakresów. Tworzy dziesięć milionów rekordów w pięć sekund na moim serwerze. Działa również z wartościami ujemnymi.
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);
Jest to przydatne również w przypadku zakresów dat i godzin:
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'));
Możesz użyć na nim sprzężenia krzyżowego, aby podzielić rekordy na podstawie wartości w tabeli. Na przykład, aby utworzyć rekord dla każdej minuty w zakresie czasu w tabeli, możesz zrobić coś takiego:
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;
Najlepsza opcja, z której korzystałem, jest następująca:
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
Wygenerowałem miliony rekordów za pomocą tego i działa idealnie.
To działa dla mnie!
select top 50 ROW_NUMBER() over(order by a.name) + 1000 as Rcount
from sys.all_objects a
sys.all_objects
- w przypadku małych zakresów <2000 elementów nie stanowi to problemu. Nie jesteś pewien, czy będzie miał problemy z uprawnieniami? idealne do szybkiego generowania partii danych testowych.
select top 50 ROW_NUMBER() over(order by a.name) + 1000 as Rcount from sys.all_objects a, sys.all_objects b
. Tam, gdzie wcześniej mogłem wygenerować tylko 2384 wiersze, teraz mogę wygenerować wiersze 5683456.
Najlepszym sposobem jest użycie rekurencyjnych 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)
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
Jeśli nie masz problemu z instalacją zestawu CLR na serwerze, dobrym rozwiązaniem jest napisanie funkcji wartościowanej tabelowo w .NET. W ten sposób możesz użyć prostej składni, dzięki czemu łatwo będzie łączyć się z innymi zapytaniami, a jako bonus nie marnujesz pamięci, ponieważ wynik jest przesyłany strumieniowo.
Utwórz projekt zawierający następującą klasę:
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() { }
}
}
Umieść zestaw gdzieś na serwerze i uruchom:
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];
Teraz możesz biegać:
select * from dbo.seq(1, 1000000)
Nic nowego, ale przepisałem rozwiązanie Briana Presslera, aby było przyjemniejsze dla oka, może się komuś przydać (nawet jeśli to tylko przyszły mnie):
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()
nie mają tego problemu.
2 lata później, ale okazało się, że mam ten sam problem. Oto jak to rozwiązałem. (edytowane w celu uwzględnienia parametrów)
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)
Odpowiedź slartidana można poprawić, pod względem wydajności, poprzez wyeliminowanie wszelkich odniesień do produktu kartezjańskiego i użycie ROW_NUMBER()
zamiast tego ( porównanie planu wykonania ):
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
Zawiń go wewnątrz CTE i dodaj klauzulę where, aby wybrać żądane liczby:
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
. W niektórych przypadkach może to obniżyć wydajność.
Oto kilka całkiem optymalnych i kompatybilnych rozwiązań:
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 where spt_values.number between @min and @max
?
Wiem, że spóźniłem się o 4 lata, ale natknąłem się na inną alternatywną odpowiedź na ten problem. Problem z szybkością to nie tylko wstępne filtrowanie, ale także zapobieganie sortowaniu. Możliwe jest wymuszenie wykonania kolejności łączenia w taki sposób, że iloczyn kartezjański faktycznie liczy się w wyniku łączenia. Wykorzystując odpowiedź Slartidana jako punkt wyjścia:
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
Jeśli znamy żądany zakres, możemy go określić za pomocą @Upper i @Lower. Łącząc wskazówkę o sprzężeniu REMOTE wraz z TOP, możemy obliczyć tylko podzbiór wartości, które chcemy, i nic nie zostanie zmarnowane.
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
Wskazówka dotycząca łączenia REMOTE wymusza na optymalizatorze porównanie najpierw po prawej stronie sprzężenia. Określając każde sprzężenie jako ZDALNE od najbardziej do najmniej znaczącej wartości, samo sprzężenie będzie poprawnie liczyć w górę o jeden. Nie ma potrzeby filtrowania za pomocą GDZIE lub sortowania według ZAMÓWIENIA.
Jeśli chcesz zwiększyć zakres, możesz kontynuować dodawanie dodatkowych sprzężeń o stopniowo wyższych rzędach wielkości, o ile są uporządkowane od najbardziej do najmniej znaczących w klauzuli FROM.
Zauważ, że jest to zapytanie specyficzne dla SQL Server 2008 lub nowszego.
Najlepsza prędkość podczas wykonywania zapytania
DECLARE @num INT = 1000
WHILE(@num<1050)
begin
INSERT INTO [dbo].[Codes]
( Code
)
VALUES (@num)
SET @num = @num + 1
end
rekurencyjne CTE o rozmiarze wykładniczym (nawet przy domyślnej rekursji 100, może to zbudować do 2 ^ 100 liczb):
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
i endnum
powinienem być wprowadzany przez użytkownika?
Musiałem wstawić ścieżkę pliku obrazu do bazy danych przy użyciu podobnej metody. Poniższe zapytanie zadziałało dobrze:
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
Kod dla ciebie byłby:
DECLARE @num INT = 1000
WHILE(@num<1051)
begin
SELECT @num
SET @num = @num + 1
end
To właśnie robię, jest to dość szybkie i elastyczne i nie zawiera dużo kodu.
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;
Zauważ, że (ORDER BY @count) jest atrapą. To nic nie robi, ale ROW_NUMBER () wymaga ORDER BY.
Edycja : zdałem sobie sprawę, że pierwotnym pytaniem było uzyskanie zakresu od x do y. Mój skrypt można zmodyfikować w ten sposób, aby uzyskać zakres:
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
Działa to tylko w przypadku sekwencji, o ile tabela aplikacji zawiera wiersze. Załóżmy, że chcę sekwencję od 1..100 i mam tabelę aplikacji dbo.foo z kolumną (typu numerycznego lub łańcuchowego) foo.bar:
select
top 100
row_number() over (order by dbo.foo.bar) as seq
from dbo.foo
Pomimo swojej obecności w klauzuli order by, dbo.foo.bar nie musi mieć odrębnych ani nawet niezerowych wartości.
Oczywiście SQL Server 2012 ma obiekty sekwencyjne, więc ten produkt jest naturalnym rozwiązaniem.
Oto, co wymyśliłem:
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
Generuje maksymalnie 2 ^ 24 wartości. Warunki łączenia utrzymują go szybko dla małych wartości.
Ukończyło się to dla mnie w 36 sekund na naszym serwerze DEV. Podobnie jak odpowiedź Briana, skupienie się na filtrowaniu do zakresu jest ważne od samego początku zapytania; a BETWEEN nadal próbuje wygenerować wszystkie początkowe rekordy przed dolną granicą, nawet jeśli ich nie potrzebuje.
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
Zauważ, że ROW_NUMBER to bigint , więc nie możemy przejść przez 2 ^^ 64 (== 16 ^^ 16) wygenerowanych rekordów żadną metodą, która go używa. Dlatego to zapytanie przestrzega tego samego górnego limitu generowanych wartości.
Używa kodu proceduralnego i funkcji wycenianej w tabeli. Powolny, ale łatwy i przewidywalny.
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
Stosowanie:
SELECT * FROM dbo.Sequence (3,7);
ID
3
4
5
6
7
Jest to tabela, więc możesz jej używać w połączeniach z innymi danymi. Najczęściej używam tej funkcji jako lewej strony sprzężenia z GRUPĄ według godziny, dnia itp., Aby zapewnić ciągłą sekwencję wartości czasu.
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
(...)
Wydajność jest mało inspirująca (16 sekund na milion wierszy), ale wystarczająca do wielu celów.
SELECT count(1) FROM [dbo].[Sequence] (
1000001
,2000000)
GO
Oracle 12c; Szybki, ale ograniczony:
select rownum+1000 from all_objects fetch first 50 rows only;
Uwaga : ograniczona do liczby wierszy widoku all_objects;
Rozwiązanie, które opracowałem i używam już od jakiegoś czasu (jeżdżę na udostępnionych pracach innych) jest nieco podobne do przynajmniej jednego opublikowanego. Nie odwołuje się do żadnych tabel i zwraca nieposortowany zakres do 1048576 wartości (2 ^ 20) i w razie potrzeby może zawierać negatywy. W razie potrzeby możesz oczywiście posortować wynik. Działa dość szybko, zwłaszcza na mniejszych dystansach.
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
Poniższą funkcję wykonałem po przeczytaniu tego wątku. Prosto i szybko:
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)