SQL select sayı aralıkları


19

Satır olarak bir dizi sayı elde etmeyi oldukça zor buldum MySQL.

Örneğin 1-5 aralığına şu şekilde ulaşılır:

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

sonuçlanacak:

1
2
3
4
5

0-99 için iki 0-9 tabloyu birleştirebilirim:

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

Tüm bunları yazmaktan UNIONve kodu küçültmenin bir yolunu bulmaktan yoruldum .

Herhangi bir fikir MySQL veya herhangi bir SQL sözdizimi golf (örneğin 0-1.000.000 aralığı)?

Şunlar için ekstra puan verilir:

  • tek açıklama
  • prosedür yok
  • değişken yok
  • DDL ifadesi yok
  • sadece DQL ifadeleri

2
Bunun meta'ya mı yoksa dba.stackexchange.com'a mı yoksa SQL iş parçacığında Golf için ipuçlarına mı ait olduğundan emin değilim .
BradC

8
Seçmenleri kapatmak için: Bu konu üzerine yapılan bir mücadeledir; golf koduyla ilgili zorluklar olmayan sorular, konuyla ilgili ipuçları soruları olarak kabul edilir.
HyperNeutrino

3
SO'dan gelen bu yanıtı beğendim . En iyi şekilde hackleyin, ama sonuçta bir golf çözümü istediniz.
Arnauld

@ İnanılmaz harika!
Dimgold

2
“Herhangi bir SQL” PostgreSQL içeriyorsa, bkz generate_series(). Burada birkaç kullanım örneğimiz var.
manatwork

Yanıtlar:


9

Sqlite gibi özyinelemeli CTE'leri destekleyen SQL lehçeleri için aşağıdakine benzer bir şey yapabilirsiniz:

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

Bu, varolan herhangi bir tabloya bağlı değildir ve LIMIT yantümcesini istediğiniz gibi değiştirebilirsiniz. Başlangıçta bunun bir varyantını StackOverflow'da gördüm.


2
Mükemmel. İşte MS SQL'de çalışan golfçü bir sürüm: WITH t AS(SELECT 1n UNION ALL SELECT n+1FROM t WHERE n<36)SELECT n FROM t Farklı uç noktalar için 1ve öğelerini 36istediğiniz gibi değiştirin.
BradC

1
Hata! MS SQL'de 100'den fazla satır istiyorsanız, option (maxrecursion 0)yukarıdaki ifademin sonuna eklemeniz gerekebilir , aksi takdirde 100'den fazla özyineleme için hata verir. ( maxrecursionBelirli bir değere veya sonsuza izin vermek için 0'a ayarlayın). .
BradC

6

@ BradC'nin yöntemine benzer .

Ben [master]-1 ile 2048 arasında bir sayı aralığı içeren bir tablo olan MS SQL kullandım BETWEEN. Aralığı oluşturmak için işleç kullanabilirsiniz .

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

Bu golf oynamak istiyorsanız, şunları yapabilirsiniz:

SELECT TOP 5 ROW_NUMBER()OVER(ORDER BY number)FROM master..spt_values

1
Golf için 2 bayt tasarruf edersinizWHERE number>0AND number<21
BradC

Neden farklı kullanıyorsunuz? Gereksiz görünüyor.
Sihirli Ahtapot Urn

1
@MagicOctopusUrn Çünkü bu tabloda yinelenen sayılar var.
Oliver

1
Evet, ya DISTINCT kullanmanız ya da WHERE type = 'P' kullanmanız gerekir. Farklı biraz daha kısadır.
BradC

1
@BradC veyaSELECT DISTINCT(number+2)... WHERE number<19
Peter Taylor


4

Bu gönderiden harika bir seçenek (@Arnauld tarafından bulundu):

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

Benim için - meydan okumayı hemen hemen çözüyor.


Bu, zaten idçok büyük değerlerle doldurulmuş bir alanı olan mevcut bir tabloya dayanıyor gibi görünüyor . Çok güzel veritabanına özgü ve eğer birisi ürün kimliğini = 4021'i sildiyse bir satırı kaçırabilirsiniz.
BradC

Evet, ama nispeten küçük aralıklar için gerçekten iyi (günler için 1-7, aylar için 1-12 vb ...)
Dimgold

4

PostgreSQL'e özel

generate_series()bir küme oluşturur, böylece yalnızca fromyan tümcesinde değil , bir kümenin oluşabileceği herhangi bir yerde de kullanabilirsiniz:

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

İşlemleri doğrudan sette de yapabilirsiniz:

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

Birden fazla set aynı uzunlukta ise, bunları paralel olarak geçebilirsiniz:

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

Farklı uzunluklarda setler için bir Kartezyen ürün üretilir:

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)

Ancak bunları fromyan tümcede kullanırsanız, aynı uzunluk kümeleri için de Kartezyen ürün alırsınız:

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)

Ayrıca zaman damgaları kümesi oluşturabilir. Örneğin, 2000-06-30 tarihinde doğdunuz ve bir hafta sonu doğum gününüzü hangi yıllarda kutladığınızı bilmek istiyorsunuz:

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)

3

MS SQL masterveritabanında adlandırılmamış bir sistem tablosu vardır spt_values. Diğer şeylerin yanı sıra, 0 ila 2047 arasında bir sayı aralığı içerir:

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

Kendi başına bir sayı tablosu olarak kullanışlıdır, ancak bir CTE'de oldukça hızlı bazı büyük sayılar elde edebilirsiniz:

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

3

(Bunlar MS-SQL'de çalışır, mySQL veya diğer platformlar için çalışıp çalışmadığından emin değildir.)

Daha küçük setler için (sipariş edilmiş veya sipariş edilmemiş) yapıcıyı kullanın VALUES:

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

(Her şey için çalışır, ancak dizeler tekrarlanan tek tırnaklarla oldukça uzun sürebilir.)

Daha sonra, adlandırılmış bir CTE (ortak tablo ifadesi) kullanarak çapraz çarpabilirsiniz, böylece tekrarlamanız gerekmez:

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

Dışarıda tonlarca başka teknik var, çoğu golf için optimize edilmemiş olsa da, "sayı tablosu üreten SQL" i arayın.


1
Bu, a limit Yile rasgele aralıklar oluşturmak için çalışır mı?
Rod

1
@ Çubuk MS-SQL'de kullanmanız gerekecekSELECT TOP 250 ...
BradC

Oh, MSSQL üstbilgisini görmedim = X
Rod

MySQL üzerinde çalışmıyor, ancak yine de yararlı :)
Dimgold

2

Bir seçenek daha, bu MS SQL 2016 ve üstü için özel:

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

Bunu muhtemelen dizeler listesi için daha kullanışlı bulacağım, ancak sayılarla da yararlı olacağı yolları görebiliyorum.


2

T-SQL, 98 bayt

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
  • ✓ tek açıklama
  • Procedures prosedür yok
  • Variables değişken yok
  • DD DDL bildirimi yok
  • ✓ sadece DQL ifadeleri

Bu langelgjm cevabının güzel bir düzenli T-SQL sürümüdür . Üsler de düzgün bir numara.
BradC

1

SQL Server için başka bir ...

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;
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.