SQL wybierz zakresy liczb


19

Trudno mi było osiągnąć zakres liczb w rzędach MySQL.

Na przykład zakres 1-5 osiąga się poprzez:

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

spowoduje:

1
2
3
4
5

dla 0-99 mogę połączyć krzyżowo dwie tabele 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

Mam dość pisania tych wszystkich UNIONi szukania sposobu na zmniejszenie kodu.

Jakieś pomysły na grę w golfa (na przykład zakres 0–1 000 000) w MySQL lub w dowolnej składni SQL?

Dodatkowe punkty są przyznawane za:

  • pojedyncze oświadczenie
  • bez procedur
  • bez zmiennych
  • brak instrukcji DDL
  • tylko instrukcje DQL

2
Nie jestem pewien, czy należy to do meta, dba.stackexchange.com czy może do wskazówek dotyczących gry w golfa w wątku SQL .
BradC

8
Aby zamknąć wyborców: jest to wyzwanie na dany temat; pytania, które nie są wyzwaniami związanymi z kodem golfowym, są uważane za pytania na temat wskazówek.
HyperNeutrino

3
Podoba mi się ta odpowiedź od SO . W najlepszym razie hackish, ale w końcu poprosiłeś o rozwiązanie do gry w golfa.
Arnauld

@Arnauld to niesamowite!
Dimgold

2
Jeśli „dowolny SQL” zawiera PostgreSQL, zobacz generate_series(). Mamy tutaj kilka przykładów użycia .
manatwork

Odpowiedzi:


9

W przypadku dialektów SQL, które obsługują rekurencyjne CTE, takie jak sqlite, możesz wykonać następujące czynności:

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

Nie zależy to od żadnej istniejącej tabeli i można dowolnie zmienić klauzulę LIMIT. Pierwotnie widziałem wariant tego na StackOverflow.


2
Doskonały. Oto wersja gry w golfa, która działa w MS SQL: WITH t AS(SELECT 1n UNION ALL SELECT n+1FROM t WHERE n<36)SELECT n FROM t dla różnych punktów końcowych, po prostu zmień 1i 36na cokolwiek chcesz.
BradC

1
Ups, jeśli chcesz więcej niż 100 wierszy w MS SQL, może być konieczne dodanie option (maxrecursion 0)na końcu mojej powyższej instrukcji, w przeciwnym razie wystąpi błąd rekursji powyżej 100. (Ustaw maxrecursionna określoną wartość lub na 0, aby zezwolić na nieskończoność) .
BradC

6

Podobne do metody @ BradC .

Użyłem MS SQL, który ma tabelę [master]z zakresem liczb od -1 do 2048. Możesz użyć BETWEENoperatora do utworzenia swojego zakresu.

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

Jeśli chcesz zagrać w golfa, możesz:

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

1
Do gry w golfa oszczędzasz 2 bajty zWHERE number>0AND number<21
BradC

Dlaczego używasz wyraźnego? Wydaje się zbędny.
Magic Octopus Urn

1
@MagicOctopusUrn Ponieważ w tej tabeli są zduplikowane liczby.
Oliver

1
Tak, albo musisz użyć DISTINCT, albo WHERE type = 'P'. Wyraźny jest nieco krótszy.
BradC

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


4

Świetna opcja z tego postu (znaleziona przez @Arnauld):

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

Dla mnie - prawie rozwiązuje to wyzwanie.


Wydaje się, że jest to zależne od istniejącej tabeli, która już ma idpole wypełnione bardzo dużymi wartościami. Tak specyficzna dla bazy danych i można pominąć wiersz, jeśli, powiedzmy, ktoś usunął ID produktu = 4021.
BradC

Tak, ale to naprawdę dobre dla stosunkowo niewielkich zakresów (1-7 dni, 1-12 miesięcy itp.)
Dimgold

4

Specyficzne dla PostgreSQL

generate_series()generuje zestaw, dzięki czemu można go używać nie tylko w fromklauzuli, ale wszędzie tam, gdzie może wystąpić zestaw:

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

Możesz także wykonywać operacje bezpośrednio na zestawie:

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

Jeśli wiele zestawów ma tę samą długość, możesz je przechodzić równolegle:

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

Dla zestawów o różnych długościach generowany jest produkt kartezjański:

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)

Ale jeśli użyjesz ich w fromklauzuli, otrzymasz również produkt kartezjański dla zestawów o tej samej długości:

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)

Może także generować zestaw znaczników czasu. Na przykład urodziłeś się w dniu 2000-06-30 i chcesz wiedzieć, w których latach świętowałeś urodziny w weekend:

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 ma nieudokumentowaną tabelę systemową w masterbazie danych o nazwie spt_values. Między innymi zawiera zakres liczb od 0 do 2047:

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

Przydatny jako tablica liczb sam w sobie, ale w CTE można dość szybko uzyskać kilka dużych liczb:

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

(Działają w MS-SQL, nie jestem pewien, czy działają na mySQL lub na innych platformach.)

W przypadku mniejszych zestawów (zamówionych lub niezamówionych) użyj VALUESkonstruktora:

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

(Działa to na wszystko, chociaż ciągi znaków mogą się wydłużać przy wszystkich powtarzających się pojedynczych cudzysłowach.)

Następnie możesz pomnożyć krzyżowo, używając nazwanego CTE (wspólne wyrażenie tabelowe), więc nie musisz go powtarzać:

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

Istnieje mnóstwo innych technik, poszukaj „SQL generujący tabelę liczb”, chociaż większość nie jest zoptymalizowana do gry w golfa.


1
Czy to zadziałałoby, limit Ytworząc dowolne zakresy?
Rod

1
@Rod W MS-SQL należy użyćSELECT TOP 250 ...
BradC

Och, nie widziałem nagłówka MSSQL = X
Rod

nie działa na MySQL, ale nadal jest przydatny :)
Dimgold

2

Jeszcze jedna opcja, ta specyficzna dla MS SQL 2016 i nowszych:

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

Prawdopodobnie uznaję to za bardziej przydatne w przypadku list ciągów, ale widzę, że przydałoby się to również w przypadku liczb.


2

T-SQL, 98 bajtów

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
  • ✓ pojedyncze oświadczenie
  • ✓ brak procedur
  • ✓ brak zmiennych
  • ✓ brak instrukcji DDL
  • ✓ tylko instrukcje DQL

To jest ładna, uporządkowana wersja T-SQL odpowiedzi langelgjm . Potęgi są również fajną sztuczką.
BradC

1

Kolejny dla 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;
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.