Bagaimana cara menghasilkan kisaran angka antara dua angka?


148

Saya memiliki dua angka sebagai masukan dari pengguna, seperti misalnya 1000dan 1050.

Bagaimana cara menghasilkan angka di antara dua angka ini, menggunakan kueri sql, dalam baris terpisah? Saya ingin ini:

 1000
 1001
 1002
 1003
 .
 .
 1050

Jawaban:


167

Pilih nilai yang tidak ada dengan VALUESkata kunci. Kemudian gunakan JOINs untuk menghasilkan banyak sekali kombinasi (dapat diperpanjang untuk membuat ratusan ribu baris dan seterusnya).

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

Alternatif yang lebih pendek, yang tidak mudah dipahami:

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


14
Ini adalah solusi yang luar biasa elegan
Aaron Hudon

9
Bisakah Anda menjelaskan sintaksnya? Apa v (n) itu?
Rafi

2
@ Rafi the v (n) dan ratusan (n) dll adalah nama / alias tabel dan kolom
Twon-ha

111

solusi alternatifnya adalah CTE rekursif:

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
Jangan mencoba menggunakan opsi maxrecusion dalam definisi tampilan. Sebaliknya Anda harus MEMILIH * DARI PILIHAN CTE_VIEW (MAXRECURSION 10000) - bermasalah, jika aplikasi klien Anda ingin menggunakan tampilan apa adanya.
TvdH

4
Ada rekursi maksimum maksimum yang disetel ke 32767 (di SQL Server 2012).
BProv

4
Hanya untuk memperjelas, jika Anda membutuhkan rekursi lebih dari 32767, maka itu dapat diatur ke 0 yang berarti nomax,
Jayvee

2
Berikut Demo untuk jawaban ini.
Stomy

7
Saya membandingkan jawaban ini dengan yang lain dan rencana Eksekusi menunjukkan bahwa jawaban ini ( memiliki biaya kueri paling sedikit dan ) adalah yang tercepat.
Stomy

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

Demo

Perhatikan bahwa tabel ini memiliki maksimum 2048 karena jumlahnya memiliki celah.

Berikut adalah pendekatan yang sedikit lebih baik menggunakan tampilan sistem (sejak dari 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

atau gunakan tabel angka khusus. Penghargaan untuk Aaron Bertrand, saya sarankan untuk membaca seluruh artikel: Hasilkan satu set atau urutan tanpa loop


2
@ user3211705: perhatikan hasil edit saya, tabel ini memiliki maksimum 2048. Saya sarankan untuk membaca seluruh artikel.
Tim Schmelter

3
Saya pikir Anda bisa menambah WHERE type = 'P'dan menghindariSELECT DISTINCT
Salman A

1
Tautan "Demo" pertama Anda terus memberi tahu sayaString index out of range: 33
slartidan

1
Kamu benar. Tapi sepertinya ada masalah dengan SqlFiddle. Apakah itu bekerja di DB Anda?
Tim Schmelter

5
Catatan singkat, kueri lintas basis data seperti ini tidak berfungsi dengan SQL Azure
Kieren Johnstone

34

Saya baru-baru ini menulis fungsi nilai tabel sebaris ini untuk memecahkan masalah ini. Itu tidak terbatas dalam jangkauan selain memori dan penyimpanan. Itu tidak mengakses tabel sehingga tidak perlu membaca atau menulis disk secara umum. Ini menambahkan nilai gabungan secara eksponensial pada setiap iterasi sehingga sangat cepat bahkan untuk rentang yang sangat besar. Ini membuat sepuluh juta catatan dalam lima detik di server saya. Ini juga bekerja dengan nilai negatif.

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

Ini berguna untuk rentang tanggal dan waktu juga:

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

Anda bisa menggunakan gabungan penerapan silang di atasnya untuk memisahkan rekaman berdasarkan nilai dalam tabel. Jadi misalnya untuk membuat rekor untuk setiap menit pada rentang waktu dalam tabel, Anda dapat melakukan sesuatu seperti:

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, kueri awal itu CEPAT. Jauh lebih cepat daripada solusi CLR yang diposting di atas. Terima kasih!
Derreck Dean

1
Bagus - Saya memiliki klien yang masih menggunakan SQL Server 2008 dan ini adalah hal yang saya butuhkan! Sangat pintar!
STLDev

1
ini bekerja untuk 1-100 tetapi kemudian gagal. Bahkan contoh Anda menghasilkan 5-500 tidak bekerja untuk saya, ini menunjukkan 5, 21, ... 484, 500
Rez.Net

3
Jika Anda ingin menyortirnya, Anda harus menambahkan urutan berdasarkan klausa:SELECT X FROM fn_ConsecutiveNumbers(5, 500) ORDER BY X;
Brian Pressler

29

Opsi terbaik yang saya gunakan adalah sebagai berikut:

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

Saya telah menghasilkan jutaan rekaman menggunakan ini dan bekerja dengan sempurna.


2
Ini adalah solusi yang paling elegan di sini, tetapi saya pikir sulit bagi banyak orang untuk memahaminya (saya telah melakukan ini dengan master.sys.all_columns). @STLDeveloper, ya ini berfungsi dengan 2008 dan lebih baru.
Cetin Basoz

14

Ini berhasil untuk saya!

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

2
Satu baris yang bagus - tetapi berhati-hatilah bahwa jumlah baris maksimal akan bergantung pada sys.all_objects- untuk rentang kecil <2000 item, ini bukan masalah. Tidak yakin apakah akan ada masalah perizinan? sempurna untuk menghasilkan sekumpulan data uji dengan cepat.
Freedomn-m

@ freedomn-m Salah satu cara untuk meningkatkan baris maksimum adalah dengan melakukan gabungan silang sendiri. select top 50 ROW_NUMBER() over(order by a.name) + 1000 as Rcount from sys.all_objects a, sys.all_objects b. Jika sebelumnya saya hanya dapat menghasilkan 2.384 baris, sekarang saya dapat menghasilkan 5683456 baris.
Klicker

10
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
Apakah ini berbeda dengan jawaban @Jayvee?
Noel

1
Ya di mana kondisinya disebut num + 1 <1050 yang akan mencetak hingga 1049 saja.
Sowbarani Karthikeyan

2
Sebuah suntingan (atau komentar) untuk jawaban yang ada yang penting sama akan memberikan nilai lebih dari jawaban yang sama sekali baru.
Noel

9

Cara terbaik adalah menggunakan ctes rekursif.

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
Ini sangat berguna. Saya memodifikasi kodenya sehingga saya dapat memasukkan 100.000 baris. Dengan solusi saya, butuh waktu 13 menit; menggunakan milik Anda, butuh lima detik. Muchísimas gracias.
Cthulhu

3
Sebenarnya, CTE rekursif adalah salah satu cara penghitungan terburuk. Mereka bahkan dapat dikalahkan oleh While Loop dalam sebuah transaksi dan While Loop akan menghasilkan pembacaan yang jauh lebih sedikit. Metode cCTE (Cascading CTE, aslinya oleh Itizik Ben-Gan) jauh lebih cepat dan menghasilkan pembacaan nol.
Jeff Moden

7

Jika Anda tidak memiliki masalah menginstal perakitan CLR di server Anda, opsi yang baik adalah menulis fungsi nilai tabel di .NET. Dengan begitu Anda dapat menggunakan sintaks sederhana, membuatnya mudah untuk digabungkan dengan kueri lain dan sebagai bonus tidak akan membuang memori karena hasilnya dialirkan.

Buat proyek yang berisi kelas berikut:

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

Letakkan perakitan di suatu tempat di server dan jalankan:

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

Sekarang Anda dapat menjalankan:

select * from dbo.seq(1, 1000000)

1
Saya mencoba solusi ini dan berhasil dengan baik, hanya saja tidak super cepat. Jika Anda hanya menghasilkan 1.000 angka, atau mungkin 10.000, itu cukup cepat. Jika Anda seperti saya dan harus menghasilkan miliaran angka, solusi Brian Pressler di bawah ini luar biasa cepat dibandingkan dengan SQL CLR.
Derreck Dean

2
@DerreckDean Anda benar. Saya pikir ini adalah solusi terbaik karena mudah dibuat dan digunakan (dan secepat yang Anda katakan). Dalam kasus saya, saya sudah memiliki rakitan untuk menggabungkan string jadi saya hanya menambahkannya di sana.
AlexDev

1
Saya juga memiliki rakitan yang sudah ada dan mencoba kedua metode tersebut. Saya membuat jumlah angka yang tidak dapat ditentukan untuk ditambahkan ke tanggal (pada dasarnya, saya membuat ulang penjadwal agen server SQL untuk menghasilkan tanggal untuk aplikasi internal kami, dan 100 tingkat rekursi tidak akan memotongnya untuk menghasilkan beberapa tahun datetimes, mungkin hingga yang kedua.), jadi saya dapat menguji beberapa solusi secara menyeluruh dari utas ini. Saya menghargai kontribusi Anda!
Derreck Dean

7

Tidak ada yang baru selain saya menulis ulang solusi Brian Pressler agar lebih enak dipandang, mungkin berguna bagi seseorang (meskipun itu hanya saya di masa depan):

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
Saya yakin Anda telah menyaring inti dari algoritme yang indah menjadi beberapa kode yang benar-benar cantik.
Clay

1
Hasilnya disusun dalam urutan yang aneh tetapi tidak kacau. Ujilah pada rentang dari 5 hingga 500. Hasilnya 5,21,37, ..., 245,6,22, ... Tahukah Anda bagaimana pemesanan akan memengaruhi kinerja? Solusi berdasarkan ROW_NUMBER()tidak memiliki masalah itu.
Przemyslaw Remin

1
Saya bukan ahli tetapi secara intuitif saya akan menebak SQL server perlu memasukkan semua hasil dalam memori dan memesannya sebelum mengembalikannya sehingga lebih banyak penggunaan memori dan respons tertunda dibandingkan dengan hanya mengalirkan hasil saat mereka datang.
Guillaume86

6

2 tahun kemudian, tetapi saya menemukan saya memiliki masalah yang sama. Inilah cara saya menyelesaikannya. (diedit untuk memasukkan parameter)

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

Jawaban slartidan dapat ditingkatkan, performa bijaksana, dengan menghilangkan semua referensi ke produk cartesian dan menggunakan ROW_NUMBER()sebagai gantinya ( perbandingan rencana eksekusi ):

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

Bungkus di dalam CTE dan tambahkan klausa where untuk memilih nomor yang diinginkan:

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 hanya dimulai pada 1. Bagaimana kita bisa memulai dari nol dengan metode Anda?
Stomy

2
@stomy SELECT ROW_NUMBER() OVER (...) - 1 AS n. Dalam beberapa kasus, ini mungkin mematikan kinerja.
Salman A

4

Berikut adalah beberapa solusi yang cukup optimal dan kompatibel:

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
Apakah metode ini entah bagaimana lebih baik daripada sekadar selecting where spt_values.number between @min and @max?
underscore_d

2
Type = 'P' filter diperlukan untuk mencegah nomor duplikat. Dengan filter ini tabel akan mengembalikan angka 0 - 2047. Jadi filter "angka antara @min dan @max" akan berfungsi selama variabel berada dalam kisaran itu. Solusi saya akan memungkinkan Anda untuk mendapatkan hingga 2048 baris dalam kisaran integer (-2,147,483,648) - (2,147,483,647).
jumxozizi

1
logika di atas hanya berguna jika perbedaan antara jumlah maksimum dan minimum kurang dari 2048 dan sekali dapat maksimum 2048 catatan pada satu titik waktu
Smart003

4

Saya tahu saya terlambat 4 tahun, tetapi saya menemukan jawaban alternatif lain untuk masalah ini. Masalah kecepatan bukan hanya pra-pemfilteran, tetapi juga mencegah penyortiran. Dimungkinkan untuk memaksa perintah gabungan untuk dieksekusi dengan cara yang benar-benar dihitung oleh produk Cartesian sebagai hasil dari penggabungan. Menggunakan jawaban slartidan sebagai titik lompatan:

    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

Jika kita mengetahui kisaran yang kita inginkan, kita dapat menentukannya melalui @Upper dan @Lower. Dengan menggabungkan REMOTE petunjuk gabungan bersama dengan TOP, kita hanya dapat menghitung subset dari nilai yang kita inginkan tanpa ada yang terbuang.

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

Petunjuk gabungan REMOTE memaksa pengoptimal membandingkan di sisi kanan gabungan terlebih dahulu. Dengan menentukan setiap gabungan sebagai REMOTE dari nilai paling signifikan hingga paling tidak signifikan, gabungan itu sendiri akan dihitung satu per satu dengan benar. Tidak perlu memfilter dengan WHERE, atau mengurutkan dengan ORDER BY.

Jika Anda ingin meningkatkan rentang, Anda dapat terus menambahkan gabungan tambahan dengan urutan besaran yang semakin tinggi, selama mereka diurutkan dari yang paling signifikan hingga yang paling tidak signifikan dalam klausa FROM.

Perhatikan bahwa ini adalah kueri khusus untuk SQL Server 2008 atau lebih tinggi.


1
Memang sangat bagus. Teknik yang sama dapat diterapkan pada jawaban Brian Pressler yang luar biasa dan juga penulisan ulang Guillaume86 yang indah.
Clay

3

Ini juga akan dilakukan

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

Kecepatan terbaik saat menjalankan kueri

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

3

CTE rekursif dalam ukuran eksponensial (bahkan untuk default 100 rekursi, ini dapat membangun hingga 2 ^ 100 angka):

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

Menurut OP, menurut saya @startnumdan endnumharus diinput oleh user?
JC

2

Saya harus memasukkan filepath gambar ke dalam database menggunakan metode serupa. Kueri di bawah berfungsi dengan baik:

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

Kode untuk Anda adalah:

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

    SET @num = @num + 1
end

2

Ini yang saya lakukan, cukup cepat dan fleksibel dan tidak banyak kode.

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;

Perhatikan bahwa (ORDER BY @count) adalah dummy. Itu tidak melakukan apa pun kecuali ROW_NUMBER () membutuhkan ORDER BY.

Sunting : Saya menyadari bahwa pertanyaan awal adalah mendapatkan rentang dari x ke y. Skrip saya dapat dimodifikasi seperti ini untuk mendapatkan kisaran:

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
Ini sangat cepat - dan fleksibel. Bekerja dengan baik untuk kebutuhan saya.
AndrewBanjo1968

2

Pembaruan untuk SQL 2017 dan yang lebih baru: Jika urutan yang Anda inginkan <8k maka ini akan berfungsi:

Declare @start_num int = 1000
,   @end_num int = 1050

Select [number] = @start_num + ROW_NUMBER() over (order by (Select null))
from string_split(replicate(' ',@end_num-@start_num-1),' ')

Elegan, dan tanpa CTE / mengakses objek sistem. Harus ditandai sebagai jawaban yang valid.
Juozas

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

Ini hanya berfungsi untuk urutan selama beberapa tabel aplikasi memiliki baris. Asumsikan saya ingin urutan dari 1..100, dan memiliki tabel aplikasi dbo.foo dengan kolom (tipe numerik atau string) foo.bar:

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

Meskipun ada dalam urutan menurut klausa, dbo.foo.bar tidak harus memiliki nilai yang berbeda atau bahkan bukan nol.

Tentu saja, SQL Server 2012 memiliki objek urutan, jadi ada solusi alami dalam produk tersebut.


1

Inilah yang saya dapatkan:

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

Menghasilkan hingga 2 ^ 24 nilai. Kondisi bergabung tetap cepat untuk nilai-nilai kecil.


1

Ini selesai untuk saya dalam 36 detik di server DEV kami. Seperti jawaban Brian, fokus pada pemfilteran ke kisaran penting dari dalam kueri; a BETWEEN masih mencoba untuk menghasilkan semua catatan awal sebelum batas bawah meskipun tidak membutuhkannya.

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

Perhatikan bahwa ROW_NUMBER adalah bigint , jadi kita tidak bisa melewati 2 ^^ 64 (== 16 ^^ 16) menghasilkan record dengan metode apa pun yang menggunakannya. Oleh karena itu, kueri ini menghormati batas atas yang sama pada nilai yang dihasilkan.


1

Ini menggunakan kode prosedural dan fungsi nilai tabel. Lambat, tetapi mudah dan dapat diprediksi.

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

Pemakaian:

SELECT * FROM dbo.Sequence (3,7);
ID
3
4
5
6
7

Ini adalah tabel, jadi Anda bisa menggunakannya dalam gabungan dengan data lain. Saya paling sering menggunakan fungsi ini sebagai sisi kiri gabungan terhadap GROUP BY jam, hari, dll untuk memastikan urutan nilai waktu yang berdekatan.

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

Performanya tidak menarik (16 detik untuk sejuta baris) tetapi cukup baik untuk banyak tujuan.

SELECT count(1) FROM [dbo].[Sequence] (
   1000001
  ,2000000)
GO

1

Oracle 12c; Cepat tapi terbatas:

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

Catatan : terbatas pada jumlah baris tampilan all_objects;


1

Solusi yang telah saya kembangkan dan gunakan untuk beberapa waktu sekarang (menunggangi beberapa karya bersama orang lain) sedikit mirip dengan setidaknya satu yang diposting. Itu tidak mereferensikan tabel apa pun dan mengembalikan rentang yang tidak diurutkan hingga 1048576 nilai (2 ^ 20) dan dapat menyertakan negatif jika diinginkan. Anda tentu saja dapat menyortir hasilnya jika perlu. Ini berjalan cukup cepat, terutama pada jarak yang lebih kecil.

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

Saya membuat fungsi di bawah ini setelah membaca utas ini. Sederhana dan cepat:

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)

0

Berikut adalah solusi umum dan relatif cepat yang menghasilkan bilangan bulat dari 1 hingga @n. Ini berfungsi dengan bilangan bulat positif apa pun @n(angka yang sangat besar akan menyebabkan luapan aritmatika) tanpa perlu menambah atau menghapus gabungan tabel. Itu tidak memerlukan penggunaan tabel sistem dan Anda juga tidak mengubah rekursi maksimal.

declare @n int = 10000 

;with d as (select * from (values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) x (d)),
n as ( 
    select d x from d where d > 0 and d <= @n
    union all
    select x * 10 + d from n, d where x * 10 + d <= @n
)
select x from n 

Anda dapat menambahkan order byklausa untuk mengurutkan angka.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.