Tôi có hai số là đầu vào từ người dùng, ví dụ như 1000
và 1050
.
Làm cách nào để tạo các số giữa hai số này, sử dụng truy vấn sql, trong các hàng riêng biệt? Tôi muốn điều này:
1000
1001
1002
1003
.
.
1050
Tôi có hai số là đầu vào từ người dùng, ví dụ như 1000
và 1050
.
Làm cách nào để tạo các số giữa hai số này, sử dụng truy vấn sql, trong các hàng riêng biệt? Tôi muốn điều này:
1000
1001
1002
1003
.
.
1050
Câu trả lời:
Chọn các giá trị không tồn tại với VALUES
từ khóa. Sau đó, sử dụng JOIN
s để tạo ra rất nhiều kết hợp (có thể được mở rộng để tạo ra hàng trăm ngàn hàng và hơn thế nữa).
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
Một thay thế ngắn hơn, đó là không dễ hiểu:
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
một giải pháp thay thế là CTE đệ quy:
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
Lưu ý rằng bảng này có tối đa 2048 vì sau đó các số có khoảng trống.
Đây là một cách tiếp cận tốt hơn một chút bằng cách sử dụng chế độ xem hệ thống (từ 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;
hoặc sử dụng bảng số tùy chỉnh. Tín dụng cho Aaron Bertrand, tôi đề nghị đọc toàn bộ bài viết: Tạo một tập hợp hoặc chuỗi không có vòng lặp
WHERE type = 'P'
và tránhSELECT DISTINCT
String index out of range: 33
Gần đây tôi đã viết hàm nội tuyến có giá trị này để giải quyết vấn đề này. Nó không bị giới hạn trong phạm vi ngoài bộ nhớ và lưu trữ. Nó truy cập không có bảng vì vậy không cần phải đọc hoặc ghi đĩa nói chung. Nó thêm các giá trị gia nhập theo cấp số nhân trên mỗi lần lặp, do đó, nó rất nhanh ngay cả đối với phạm vi rất lớn. Nó tạo ra mười triệu bản ghi trong năm giây trên máy chủ của tôi. Nó cũng hoạt động với các giá trị âm.
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);
Nó cũng tiện cho các phạm vi ngày và giờ:
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'));
Bạn có thể sử dụng một ứng dụng chéo tham gia vào nó để phân chia các bản ghi dựa trên các giá trị trong bảng. Vì vậy, ví dụ để tạo một bản ghi cho mỗi phút trên một phạm vi thời gian trong một bảng bạn có thể làm một cái gì đó như:
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;
Tùy chọn tốt nhất tôi đã sử dụng là như sau:
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
Tôi đã tạo ra hàng triệu bản ghi bằng cách sử dụng này và nó hoạt động hoàn hảo.
Nó làm việc cho tôi!
select top 50 ROW_NUMBER() over(order by a.name) + 1000 as Rcount
from sys.all_objects a
sys.all_objects
- đối với phạm vi nhỏ <2000 mặt hàng, đây không phải là vấn đề. Không chắc chắn nếu nó sẽ có vấn đề quyền? hoàn hảo để nhanh chóng tạo ra một loạt dữ liệu thử nghiệm.
select top 50 ROW_NUMBER() over(order by a.name) + 1000 as Rcount from sys.all_objects a, sys.all_objects b
. Trường hợp trước đây tôi chỉ có thể tạo 2384 hàng, bây giờ tôi có thể tạo 5683456 hàng.
Cách tốt nhất là sử dụng các byte đệ quy.
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
Nếu bạn không gặp vấn đề gì khi cài đặt một cụm CLR trong máy chủ của mình, một tùy chọn tốt là viết một hàm có giá trị bảng trong .NET. Bằng cách đó, bạn có thể sử dụng một cú pháp đơn giản, giúp dễ dàng tham gia với các truy vấn khác và như một phần thưởng sẽ không lãng phí bộ nhớ vì kết quả được truyền phát.
Tạo một dự án chứa lớp sau:
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() { }
}
}
Đặt lắp ráp ở đâu đó trên máy chủ và chạy:
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];
Bây giờ bạn có thể chạy:
select * from dbo.seq(1, 1000000)
Không có gì mới nhưng tôi viết lại giải pháp Brian Pressler để dễ nhìn hơn, nó có thể hữu ích với ai đó (ngay cả khi đó chỉ là tương lai với tôi):
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()
không có vấn đề đó.
2 năm sau, nhưng tôi thấy tôi có cùng một vấn đề. Đây là cách tôi giải quyết nó. (được chỉnh sửa để bao gồm các tham số)
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)
Câu trả lời của slartidan có thể được cải thiện, hiệu suất khôn ngoan, bằng cách loại bỏ tất cả các tham chiếu đến sản phẩm cartesian và sử dụng ROW_NUMBER()
thay thế ( kế hoạch thực hiện so sánh ):
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
Gói nó bên trong CTE và thêm mệnh đề where để chọn các số mong muốn:
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
. Trong một số trường hợp, điều này có thể giết chết hiệu suất.
Dưới đây là một số giải pháp khá tối ưu và tương thích:
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
?
Tôi biết tôi đã 4 năm quá muộn, nhưng tôi vấp phải một câu trả lời khác cho vấn đề này. Vấn đề về tốc độ không chỉ là lọc trước mà còn ngăn chặn việc sắp xếp. Có thể buộc đơn đặt hàng tham gia thực hiện theo cách mà sản phẩm của Cartesian thực sự được tính là kết quả của việc tham gia. Sử dụng câu trả lời của slartidan như một điểm xuất phát:
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
Nếu chúng tôi biết phạm vi chúng tôi muốn, chúng tôi có thể chỉ định nó thông qua @Upper và @Lower. Bằng cách kết hợp gợi ý tham gia XÓA cùng với TOP, chúng tôi chỉ có thể tính toán tập hợp con của các giá trị mà chúng tôi muốn mà không có gì lãng phí.
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
Gợi ý tham gia XÓA buộc phải tối ưu hóa để so sánh ở phía bên phải của phép nối trước. Bằng cách chỉ định mỗi lần tham gia là XÓA từ hầu hết đến giá trị nhỏ nhất, chính liên kết đó sẽ được tính lên một cách chính xác. Không cần phải lọc với WHERE hoặc sắp xếp theo thứ tự B BYNG.
Nếu bạn muốn tăng phạm vi, bạn có thể tiếp tục thêm các phép nối bổ sung với các bậc độ lớn dần dần, miễn là chúng được sắp xếp từ hầu hết đến ít nhất trong mệnh đề TỪ.
Lưu ý rằng đây là truy vấn dành riêng cho SQL Server 2008 trở lên.
Tốc độ tốt nhất khi chạy truy vấn
DECLARE @num INT = 1000
WHILE(@num<1050)
begin
INSERT INTO [dbo].[Codes]
( Code
)
VALUES (@num)
SET @num = @num + 1
end
CTE đệ quy ở kích thước mũ (thậm chí mặc định là 100 đệ quy, điều này có thể tạo tới 2 ^ 100 số):
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
và endnum
nên được người dùng nhập liệu?
Tôi đã phải chèn filepath hình ảnh vào cơ sở dữ liệu bằng phương pháp tương tự. Các truy vấn dưới đây hoạt động tốt:
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
Mã cho bạn sẽ là:
DECLARE @num INT = 1000
WHILE(@num<1051)
begin
SELECT @num
SET @num = @num + 1
end
Đây là những gì tôi làm, nó khá nhanh và linh hoạt và không có nhiều mã.
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;
Lưu ý rằng (ĐẶT HÀNG B @NG @count) là một hình nộm. Nó không làm gì cả nhưng ROW_NUMBER () yêu cầu ĐẶT HÀNG B .NG.
Chỉnh sửa : Tôi nhận ra rằng câu hỏi ban đầu là để có một phạm vi từ x đến y. Kịch bản của tôi có thể được sửa đổi như thế này để có được một phạm vi:
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
Điều này chỉ hoạt động cho các chuỗi miễn là một số bảng ứng dụng có hàng. Giả sử tôi muốn chuỗi từ 1..100 và có bảng ứng dụng dbo.foo với cột (thuộc loại số hoặc chuỗi) foo.bar:
select
top 100
row_number() over (order by dbo.foo.bar) as seq
from dbo.foo
Mặc dù có sự hiện diện theo thứ tự theo mệnh đề, dbo.foo.bar không phải có các giá trị riêng biệt hoặc thậm chí không có giá trị.
Tất nhiên, SQL Server 2012 có các đối tượng chuỗi, vì vậy có một giải pháp tự nhiên trong sản phẩm đó.
Đây là những gì tôi nghĩ ra:
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
Tạo tối đa 2 ^ 24 giá trị. Tham gia điều kiện giữ cho nó nhanh cho các giá trị nhỏ.
Điều này hoàn thành cho tôi trong 36 giây trên máy chủ DEV của chúng tôi. Giống như câu trả lời của Brian, tập trung vào việc lọc theo phạm vi là điều quan trọng từ trong truy vấn; một GIỮA vẫn cố gắng tạo ra tất cả các bản ghi ban đầu trước khi giới hạn dưới mặc dù nó không cần chúng.
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
Lưu ý rằng ROW_NUMBER là một bigint , vì vậy chúng tôi không thể đi qua 2 ^^ 64 (== 16 ^^ 16) hồ sơ được tạo ra bằng phương pháp bất kỳ mà sử dụng nó. Do đó, truy vấn này tôn trọng cùng giới hạn trên đối với các giá trị được tạo.
Điều này sử dụng mã thủ tục và hàm có giá trị bảng. Chậm, nhưng dễ dàng và có thể dự đoán.
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
Sử dụng:
SELECT * FROM dbo.Sequence (3,7);
ID
3
4
5
6
7
Đó là một bảng, vì vậy bạn có thể sử dụng nó trong các phép nối với dữ liệu khác. Tôi thường xuyên sử dụng chức năng này như là bên trái của một phép nối với nhóm theo giờ, ngày, vv để đảm bảo một chuỗi các giá trị thời gian liền kề.
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
(...)
Hiệu suất là không mệt mỏi (16 giây cho một triệu hàng) nhưng đủ tốt cho nhiều mục đích.
SELECT count(1) FROM [dbo].[Sequence] (
1000001
,2000000)
GO
Giải pháp mà tôi đã phát triển và sử dụng từ khá lâu nay (cưỡi một số tác phẩm được chia sẻ của người khác) hơi giống với ít nhất một bài được đăng. Nó không tham chiếu bất kỳ bảng nào và trả về phạm vi chưa được sắp xếp lên tới 1048576 giá trị (2 ^ 20) và có thể bao gồm các phủ định nếu muốn. Tất nhiên bạn có thể sắp xếp kết quả nếu cần thiết. Nó chạy khá nhanh, đặc biệt là trên phạm vi nhỏ hơn.
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
Tôi đã thực hiện các chức năng dưới đây sau khi đọc chủ đề này. Đơn giản và nhanh chóng:
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)