SQL pilih rentang angka

19

Saya merasa cukup sulit untuk mencapai kisaran angka seperti baris MySQL.

Misalnya rentang 1-5 dicapai dengan:

SELECT 1 
UNION
SELECT 2
UNION
SELECT 3
UNION
SELECT 4
UNION
SELECT 5

akan menghasilkan:

1
2
3
4
5

untuk 0-99 saya bisa bergabung dengan dua tabel 0-9:

CREATE TABLE nums as
SELECT 0 as num
UNION
SELECT 1 
UNION
SELECT 2
UNION
SELECT 3
UNION
SELECT 4
UNION
SELECT 5
UNION
SELECT 6 
UNION
SELECT 7
UNION
SELECT 8
UNION
SELECT 9
;

Select n.num*10+nums.num v 
From nums n cross join nums

Saya bosan menulis semua ini UNIONdan mencari cara untuk mengecilkan kode.

Adakah ide bagaimana membuatnya golf (misalnya rentang 0-1.000.000) di MySQL atau sintaks SQL apa pun?

Poin ekstra diberikan untuk:

  • satu pernyataan
  • tidak ada prosedur
  • tidak ada variabel
  • tidak ada pernyataan DDL
  • hanya pernyataan DQL
Dimgold
sumber
2
Tidak yakin apakah ini termasuk dalam meta, atau di dba.stackexchange.com atau mungkin di kiat untuk Golf di thread SQL .
BradC
8
Untuk menutup pemilih: Ini adalah tantangan pada topik; pertanyaan yang bukan tantangan yang terkait dengan kode golf dianggap sebagai pertanyaan kiat pada topik.
HyperNeutrino
3
Saya agak suka jawaban ini dari SO . Retas paling-paling, tetapi Anda meminta solusi golf.
Arnauld
@Arnauld itu luar biasa!
Dimgold
2
Jika "sembarang SQL" termasuk PostgreSQL, lihat generate_series(). Kami memiliki beberapa contoh penggunaan di sini.
manatwork

Jawaban:

9

Untuk dialek SQL yang mendukung CTE rekursif seperti sqlite, Anda dapat melakukan sesuatu seperti berikut:

WITH RECURSIVE f(x) AS
(
  SELECT 1 UNION ALL SELECT x + 1 FROM f LIMIT 1000000
)
SELECT x
FROM f;

Ini tidak tergantung pada tabel yang ada dan Anda dapat mengubah klausa LIMIT seperti yang diinginkan. Saya awalnya melihat varian ini di StackOverflow.

langelgjm
sumber
2
Luar biasa. Berikut adalah versi golf yang berfungsi dalam MS SQL: WITH t AS(SELECT 1n UNION ALL SELECT n+1FROM t WHERE n<36)SELECT n FROM t Untuk titik akhir yang berbeda, cukup ubah 1dan 36ke apa pun yang Anda inginkan.
BradC
1
Ups, jika Anda menginginkan lebih dari 100 baris dalam MS SQL, Anda mungkin perlu menambahkan option (maxrecursion 0)bagian akhir dari pernyataan saya di atas, jika tidak maka kesalahan untuk rekursi lebih dari 100. (Baik diset maxrecursionke nilai tertentu, atau ke 0 untuk memungkinkan tak terbatas) .
BradC
6

Mirip dengan metode @ BradC .

Saya menggunakan MS SQL, yang memiliki tabel [master]dengan kisaran angka -1 hingga 2048. Anda dapat menggunakan BETWEENoperator untuk membuat rentang Anda.

SELECT DISTINCT(number)
FROM master..[spt_values] 
WHERE number BETWEEN 1 AND 5

Jika Anda ingin bermain golf ini, Anda dapat melakukan:

SELECT TOP 5 ROW_NUMBER()OVER(ORDER BY number)FROM master..spt_values
Oliver
sumber
1
Untuk bermain golf, Anda menghemat 2 byte denganWHERE number>0AND number<21
BradC
Mengapa Anda menggunakan yang berbeda? Tampaknya berlebihan.
Guci Gurita Ajaib
1
@MagicOctopusUrn Karena ada angka duplikat di tabel itu.
Oliver
1
Yap Anda harus menggunakan DISTINCT atau menggunakan WHERE type = 'P'. Perbedaannya sedikit lebih pendek.
BradC
1
@BradC, atauSELECT DISTINCT(number+2)... WHERE number<19
Peter Taylor
5

PostgreSQL, 35 byte

PostgreSQL memiliki kemudahan ini:

SELECT * FROM generate_series(1,5)

Jika Anda membutuhkannya beri nama:

SELECT num FROM generate_series(1,5)AS a(num)

Anda juga dapat melakukan ini dengan cap waktu. https://www.postgresql.org/docs/9.5/static/functions-srf.html

Bersepeda
sumber
2
Saya rasa saya pindah ke postgres
Dimgold
4

Opsi hebat dari pos ini (ditemukan oleh @Arnauld):

SELECT id%1000001 as num
FROM <any_large_table>
GROUP BY num

Bagi saya - ini cukup banyak memecahkan tantangan.

Dimgold
sumber
Ini tampaknya mengandalkan tabel yang sudah ada yang memiliki idbidang diisi melalui nilai yang sangat besar. Basis data yang sangat spesifik, dan Anda bisa melewatkan satu baris jika, katakanlah, seseorang menghapus ID produk = 4021.
BradC
Ya, tapi itu sangat bagus untuk rentang yang relatif kecil (1-7 untuk hari, 1-12 untuk bulan, dll ...)
Dimgold
4

Khusus postgreSQL

generate_series()menghasilkan set, sehingga Anda dapat menggunakannya bukan hanya dalam fromklausa, tetapi di mana pun di mana set dapat terjadi:

psql=# select generate_series(10, 20, 3);
 generate_series 
-----------------
              10
              13
              16
              19
(4 rows)

Anda juga dapat melakukan operasi langsung di set:

psql=# select 2000 + generate_series(10, 20, 3) * 2;
 ?column? 
----------
     2020
     2026
     2032
     2038
(4 rows)

Jika beberapa set memiliki panjang yang sama, Anda dapat melewatinya secara paralel:

psql=# select generate_series(1, 3), generate_series(4, 6);
 generate_series | generate_series 
-----------------+-----------------
               1 |               4
               2 |               5
               3 |               6
(3 rows)

Untuk set dengan panjang berbeda, dihasilkan produk Cartesian:

psql=# select generate_series(1, 3), generate_series(4, 5);
 generate_series | generate_series 
-----------------+-----------------
               1 |               4
               2 |               5
               3 |               4
               1 |               5
               2 |               4
               3 |               5
(6 rows)

Tetapi jika Anda menggunakannya dalam fromklausa, Anda mendapatkan produk Cartesian untuk set panjang yang sama juga:

psql=# select * from generate_series(1, 2), generate_series(3, 4) second;
 generate_series | second 
-----------------+--------
               1 |      3
               1 |      4
               2 |      3
               2 |      4
(4 rows)

Itu juga dapat menghasilkan set cap waktu. Misalnya Anda lahir pada 2000-06-30 dan ingin tahu tahun berapa Anda merayakan ulang tahun di akhir pekan:

psql=# select to_char(generate_series, 'YYYY - Day') from generate_series('2000-06-30', current_date, interval '1 year') where to_char(generate_series, 'D') in ('1', '7');
     to_char      
------------------
 2001 - Saturday 
 2002 - Sunday   
 2007 - Saturday 
 2012 - Saturday 
 2013 - Sunday   
(5 rows)
manatwork
sumber
3

MS SQL memiliki tabel sistem tidak terdokumentasi dalam masterdatabase yang disebut spt_values. Di antara hal-hal lain, ini berisi rentang angka dari 0 hingga 2047:

--returns 0 to 2,047
SELECT number n 
FROM master..spt_values
WHERE TYPE='P'

Berguna sebagai tabel angka hanya dengan sendirinya, tetapi dalam CTE Anda bisa mendapatkan beberapa angka besar dengan cepat:

--returns 0 to 4,194,304
WITH x AS(SELECT number n FROM master..spt_values WHERE TYPE='P')
SELECT 2048*x.a+*y.a
FROM x,x y
ORDER BY 1
BradC
sumber
3

(Ini berfungsi dalam MS-SQL, tidak yakin apakah mereka berfungsi untuk mySQL atau platform lain.)

Untuk set yang lebih kecil (dipesan atau tidak dipesan), gunakan VALUESkonstruktor:

--Generates 0-9
SELECT a 
FROM(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9))x(a)

(Ini bisa digunakan untuk apa saja, walaupun string bisa cukup panjang dengan semua tanda kutip tunggal yang diulang.)

Kemudian Anda bisa melipatgandakan menggunakan CTE bernama (ekspresi tabel umum) sehingga Anda tidak perlu mengulanginya:

--Generates 0-999
WITH x AS(SELECT a FROM(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9))x(a))
SELECT 100*x.a+10*y.a+z.a 
FROM x,x y,x z
ORDER BY 1

Ada banyak teknik lain di luar sana, cari "SQL menghasilkan tabel angka", meskipun sebagian besar tidak dioptimalkan untuk bermain golf.

BradC
sumber
1
Apakah ini bekerja dengan limit Yuntuk membuat rentang yang sewenang-wenang?
Rod
1
@Rod Dalam MS-SQL Anda harus menggunakanSELECT TOP 250 ...
BradC
Oh, saya tidak melihat header MSSQL = X
Rod
tidak berfungsi pada MySQL, tetapi tetap bermanfaat :)
Dimgold
2

Satu lagi opsi, yang satu ini khusus untuk MS SQL 2016 ke atas:

SELECT value v
FROM STRING_SPLIT('1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16', ',')

Saya mungkin akan menemukan ini lebih berguna untuk daftar string, tetapi saya bisa melihat cara ini akan berguna dengan angka juga.

BradC
sumber
2

T-SQL, 98 byte

WITH H AS(SELECT 0i UNION ALL SELECT i+1FROM H WHERE i<99)SELECT H.i+1e4*A.i+B.i*1e2FROM H,H A,H B
  • ✓ pernyataan tunggal
  • ✓ tidak ada prosedur
  • ✓ tidak ada variabel
  • ✓ tidak ada pernyataan DDL
  • ✓ hanya pernyataan DQL
Aplato
sumber
Ini adalah versi T-SQL yang bagus dari jawaban langelgjm . Eksponen adalah trik yang rapi juga.
BradC
1

Lain untuk SQL Server ...

WITH 
    cte_n1 (n) AS (SELECT 1 FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) n (n)),   -- 10
    cte_n2 (n) AS (SELECT 1 FROM cte_n1 a CROSS JOIN cte_n1 b),                             -- 100
    cte_Tally (n) AS (
        SELECT TOP (<how many ROWS do you want?>)
            ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
        FROM
            cte_n2 a CROSS JOIN cte_n2 b                                                    -- 10,000
        )
SELECT 
    t.n
FROM
    cte_Tally t;
Jason A. Long
sumber