Làm thế nào để tạo ra một phạm vi số giữa hai số?


141

Tôi có hai số là đầu vào từ người dùng, ví dụ như 10001050.

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:


159

Chọn các giá trị không tồn tại với VALUEStừ khóa. Sau đó, sử dụng JOINs để 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

Demo

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

Demo


13
Đây là một giải pháp tuyệt vời tao nhã
Aaron Hudon

9
Bạn có thể giải thích cú pháp? V (n) là gì?
Rafi

2
@Rafi v (n) và hàng trăm (n) v.v ... là tên / bí danh của bảng và cột
Twon-ha

106

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)

4
Đừng cố sử dụng tùy chọn maxrecusion trong định nghĩa chế độ xem. Thay vào đó, bạn phải CHỌN * TỪ CTE_VIEW TÙY CHỌN (MAXRECURSION 10000) - có vấn đề, nếu ứng dụng khách của bạn muốn sử dụng chế độ xem như hiện tại.
TvdH

4
Có một tối đa maxrecursion thiết lập để 32767 (trong SQL Server 2012).
BProv

4
Chỉ cần làm rõ, nếu bạn cần một đệ quy của hơn 32767, sau đó nó có thể được đặt thành 0 mà phương tiện nomax,
Jayvee

2
Đây là bản demo cho câu trả lời này.
stomy

7
Tôi đã so sánh câu trả lời này với các câu hỏi khác và kế hoạch Thi hành cho thấy câu trả lời này ( có chi phí truy vấn ít nhất và ) là nhanh nhất.
stomy

39
SELECT DISTINCT n = number 
FROM master..[spt_values] 
WHERE number BETWEEN @start AND @end

Demo

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;

Demo

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


2
@ user3211705: chú ý chỉnh sửa của tôi, bảng này có tối đa 2048. Tôi đề nghị đọc toàn bộ bài viết.
Tim Schmelter

3
Tôi nghĩ bạn có thể thêm WHERE type = 'P'và tránhSELECT DISTINCT
Salman A

1
Liên kết "Demo" đầu tiên của bạn tiếp tục nói với tôiString index out of range: 33
slartidan

1
Bạn đúng. Nhưng nó có vẻ là một vấn đề với SqlFiddle. Nó có hoạt động trong DB của bạn không?
Tim Schmelter

4
Lưu ý nhanh, các truy vấn cơ sở dữ liệu chéo như thế này không hoạt động với SQL Azure
Kieren Johnstone

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

1
Wow, truy vấn ban đầu đó là NHANH CHÓNG. Nhanh hơn nhiều so với giải pháp CLR được đăng ở trên. Cảm ơn!
Derreck Dean

1
Rất vui - Tôi vẫn là khách hàng trên SQL Server 2008 và đây là thứ tôi cần! Rất thông minh!
STLDev

1
nó hoạt động cho 1-100 nhưng sau đó thất bại. Ngay cả ví dụ về việc tạo 5-500 của bạn cũng không hiệu quả với tôi, nó hiển thị 5, 21, ... 484, 500
Rez.Net

3
Nếu bạn muốn nó được sắp xếp, bạn sẽ phải thêm một đơn đặt hàng theo mệnh đề:SELECT X FROM fn_ConsecutiveNumbers(5, 500) ORDER BY X;
Brian Pressler

29

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.


2
Đây là giải pháp tao nhã nhất ở đây, nhưng tôi nghĩ thật khó để nhiều người hiểu nó (tôi đã từng làm điều này với master.sys.all_columns). @STLDeveloper, vâng, nó hoạt động với 2008 và mới hơn.
Cetin Basoz

13

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

2
Một lớp lót đẹp - nhưng được cảnh báo rằng số lượng hàng tối đa sẽ phụ thuộc vào 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.
Freedomn-m

@ Freedomn-m Một cách để tăng các hàng tối đa là thực hiện tham gia tự chéo. 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.
Klicker

9

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.


1
Điều này rất hữu ích. Tôi đã sửa đổi mã để tôi có thể chèn 100.000 hàng. Với giải pháp của tôi mất khoảng 13 phút; sử dụng của bạn, mất năm giây. Rất nhiều gracias.
Cthulhu

2
Trên thực tế, CTE đệ quy là một trong những cách tồi tệ nhất để đếm. Họ thậm chí có thể bị đánh bại bởi Vòng lặp While trong giao dịch và Vòng lặp While sẽ tạo ra số lần đọc ít hơn rất nhiều. Phương pháp cCTE (Cascading CTEs, ban đầu bởi Itizik Ben-Gan) nhanh hơn nhiều và không đọc được.
Jeff Moden

9
declare @start int = 1000
declare @end    int =1050

;with numcte  
AS  
(  
  SELECT @start [SEQUENCE]  
  UNION all  
  SELECT [SEQUENCE] + 1 FROM numcte WHERE [SEQUENCE] < @end 
)      
SELECT * FROM numcte

1
Điều này có khác với câu trả lời của @Jayvee không?
Noel

1
Vâng, trong trường hợp điều kiện được đề cập là num + 1 <1050, nó sẽ chỉ in tối đa 1049.
Sowbarani Karthikeyan

2
Một chỉnh sửa (hoặc bình luận) cho câu trả lời hiện có là điều cần thiết giống nhau sẽ cung cấp nhiều giá trị hơn một câu trả lời hoàn toàn mới.
Noel

7

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)

1
Tôi đã thử giải pháp này và nó hoạt động tốt, chỉ là không siêu nhanh. Nếu bạn đang tạo chỉ 1.000 số, hoặc có thể 10.000, thì khá nhanh. Nếu bạn giống tôi và phải tạo ra hàng tỷ số, giải pháp của Brian Pressler dưới đây nhanh đến mức không thể tin được so với SQL CLR.
Derreck Dean

2
@DerreckDean Bạn nói đúng. Tôi nghĩ rằng anh ấy là giải pháp tốt nhất bởi vì nó dễ dàng để tạo và sử dụng (và nhanh như bạn nói). Trong trường hợp của tôi, tôi đã có một hội đồng để nối các chuỗi nên tôi chỉ cần thêm nó vào đó.
AlexDev

1
Tôi đã có một hội đồng hiện có là tốt và đã thử cả hai phương pháp. Tôi đang tạo một số lượng không xác định để thêm vào ngày (về cơ bản, tôi đã tạo lại bộ lập lịch đại lý máy chủ SQL để tạo ngày cho ứng dụng nội bộ của chúng tôi và 100 cấp đệ quy sẽ không cắt giảm để tạo ra nhiều năm datetimes, có thể xuống đến lần thứ hai.), vì vậy tôi có thể kiểm tra kỹ lưỡng nhiều giải pháp từ chuỗi này. Tôi đánh giá cao sự đóng góp của bạn!
Derreck Dean

7

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

1
Tôi tin rằng bạn đã chắt lọc bản chất của một thuật toán đẹp vào một số mã đẹp hoàn toàn.
Clay

1
Các kết quả được sắp xếp theo thứ tự kỳ lạ nhưng không hỗn loạn. Kiểm tra nó trên phạm vi từ 5 đến 500. Nó trả về 5,21,37, ..., 245,6,22, ... Bạn có biết việc đặt hàng sẽ ảnh hưởng đến hiệu suất như thế nào không? Các giải pháp dựa trên ROW_NUMBER()không có vấn đề đó.
Przemyslaw Remin

1
Tôi không phải là một chuyên gia nhưng theo trực giác, tôi đoán máy chủ SQL sẽ cần đặt tất cả các kết quả vào bộ nhớ và sắp xếp chúng trước khi trả lại chúng để sử dụng bộ nhớ nhiều hơn và phản hồi chậm thay vì chỉ truyền kết quả khi chúng đến.
Guillaume86

6

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)

5

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

1
ROW_NUMBER chỉ bắt đầu từ 1. Làm thế nào chúng ta có thể bắt đầu từ số 0 với phương thức của bạn?
stomy

2
@stomy 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.
Salman A

4

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
;

1
Là phương pháp này bằng cách nào đó tốt hơn chỉ đơn giản là selecting where spt_values.number between @min and @max?
gạch dưới

2
Loại bộ lọc = 'P' là bắt buộc để ngăn các số trùng lặp. Với bộ lọc này, bảng sẽ trả về các số 0 - 2047. Vì vậy, bộ lọc "số giữa @min và @max" sẽ hoạt động miễn là các biến nằm trong phạm vi đó. Giải pháp của tôi sẽ cho phép bạn nhận được tối đa 2048 hàng trong phạm vi số nguyên (-2,147,483,648) - (2,147,483,647).
jumxozizi

1
logic trên chỉ hữu ích khi chênh lệch giữa số tối đa và tối thiểu dưới 2048 và một lần có thể tối đa 2048 bản ghi tại một thời điểm
Smart003

4

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.


1
Thực sự rất tốt đẹp. Kỹ thuật tương tự cũng có thể được áp dụng cho câu trả lời xuất sắc của Brian Pressler và cách viết lại đáng yêu của Guillaume86.
Đất sét

3

Điều này cũng sẽ làm

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

3

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

3

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

Theo OP, tôi nghĩ @startnumendnumnên được người dùng nhập liệu?
JC

2

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

2

Đâ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;

1
Điều này rất nhanh - và linh hoạt. Làm việc tốt cho nhu cầu của tôi.
AndrewBanjo1968

1
-- Generate Numeric Range
-- Source: http://www.sqlservercentral.com/scripts/Miscellaneous/30397/

CREATE TABLE #NumRange(
    n int
)

DECLARE @MinNum int
DECLARE @MaxNum int
DECLARE @I int

SET NOCOUNT ON

SET @I = 0
WHILE @I <= 9 BEGIN
    INSERT INTO #NumRange VALUES(@I)
    SET @I = @I + 1
END


SET @MinNum = 1
SET @MaxNum = 1000000

SELECT  num = a.n +
    (b.n * 10) +
    (c.n * 100) +
    (d.n * 1000) +
    (e.n * 10000)
FROM    #NumRange a
CROSS JOIN #NumRange b
CROSS JOIN #NumRange c
CROSS JOIN #NumRange d
CROSS JOIN #NumRange e
WHERE   a.n +
    (b.n * 10) +
    (c.n * 100) +
    (d.n * 1000) +
    (e.n * 10000) BETWEEN @MinNum AND @MaxNum
ORDER BY a.n +
    (b.n * 10) +
    (c.n * 100) +
    (d.n * 1000) +
    (e.n * 10000) 

DROP TABLE #NumRange

1

Đ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 đó.


1

Đâ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ỏ.


1

Đ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.


1

Đ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

1

Oracle 12c; Nhanh chóng nhưng hạn chế:

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

Lưu ý : giới hạn số lượng hàng của chế độ xem all_objects;


1

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
)

1
;WITH u AS (
    SELECT Unit FROM (VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) v(Unit)
),
d AS (
    SELECT 
        (Thousands+Hundreds+Tens+Units) V
    FROM 
           (SELECT Thousands = Unit * 1000 FROM u) Thousands 
           ,(SELECT Hundreds = Unit * 100 FROM u) Hundreds 
           ,(SELECT Tens = Unit * 10 FROM u) Tens 
           ,(SELECT Units = Unit FROM u) Units
    WHERE
           (Thousands+Hundreds+Tens+Units) <= 10000
)

SELECT * FROM d ORDER BY v

1

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)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.