SQL-Auswahlnummernbereiche

19

Ich fand es ziemlich schwierig, eine Reihe von Zahlen als Zeilen zu erreichen MySQL.

Zum Beispiel wird der Bereich 1-5 erreicht durch:

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

wird darin enden, dass:

1
2
3
4
5

Für 0-99 kann ich zwei 0-9-Tische kreuzen:

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

Ich bin es leid, all diese UNIONs zu schreiben und nach einer Möglichkeit zu suchen, den Code zu verkleinern.

Irgendwelche Ideen, wie man in MySQL oder einer anderen SQL-Syntax Golf spielt (zum Beispiel im Bereich von 0-1.000.000)?

Zusätzliche Punkte werden vergeben für:

  • einzelne Aussage
  • keine Prozeduren
  • keine Variablen
  • keine DDL-Anweisungen
  • nur DQL-Anweisungen
Dimgold
quelle
2
Nicht sicher, ob dies in Meta oder in dba.stackexchange.com oder vielleicht in den Tipps für das Golfen in SQL-Thread gehört .
BradC
8
So schließen Sie die Wähler: Dies ist eine themenbezogene Herausforderung. Fragen, bei denen es sich nicht um Herausforderungen im Zusammenhang mit dem Golf-Code handelt, gelten als Fragen mit thematischen Tipps.
HyperNeutrino
3
Diese Antwort von SO gefällt mir irgendwie . Bestenfalls hackisch, aber Sie haben doch nach einer Golflösung gefragt.
Arnauld
@ Arnauld, das ist unglaublich!
Dimgold
2
Wenn "any SQL" PostgreSQL enthält, lesen Sie generate_series(). Wir haben hier einige Anwendungsbeispiele .
Manatwork

Antworten:

9

Für SQL-Dialekte, die rekursive CTEs wie SQLite unterstützen , können Sie Folgendes tun:

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

Dies hängt nicht von einer vorhandenen Tabelle ab, und Sie können die LIMIT-Klausel nach Bedarf ändern. Ich habe ursprünglich eine Variante davon auf StackOverflow gesehen.

langelgjm
quelle
2
Ausgezeichnet. Hier ist eine Golf-Version, die in MS SQL funktioniert: WITH t AS(SELECT 1n UNION ALL SELECT n+1FROM t WHERE n<36)SELECT n FROM t Ändern Sie für verschiedene Endpunkte einfach das 1und 36nach Ihren Wünschen .
BradC
1
Hoppla, wenn Sie mehr als 100 Zeilen in MS SQL wollen, müssen Sie hinzufügen option (maxrecursion 0)zu Ende meiner obigen Aussage, sonst ist es Fehler aus für Rekursion über 100. (Entweder Satz maxrecursionauf einen bestimmten Wert oder auf 0 ermöglichen unendlich) .
BradC
6

Ähnlich wie bei @ BradC .

Ich habe MS SQL verwendet, das eine Tabelle [master]mit einem Zahlenbereich von -1 bis 2048 enthält. Sie können den BETWEENOperator verwenden, um Ihren Bereich zu erstellen.

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

Wenn Sie Golf spielen möchten, können Sie:

SELECT TOP 5 ROW_NUMBER()OVER(ORDER BY number)FROM master..spt_values
Oliver
quelle
1
Zum Golfen sparen Sie 2 Bytes mitWHERE number>0AND number<21
BradC
Warum benutzt du distinct? Scheint überflüssig.
Magic Octopus Urn
1
@MagicOctopusUrn Da diese Tabelle doppelte Zahlen enthält.
Oliver
1
Ja, Sie müssen entweder DISTINCT oder WHERE type = 'P' verwenden. Deutlich ist etwas kürzer.
BradC
1
@BradC oderSELECT DISTINCT(number+2)... WHERE number<19
Peter Taylor
5

PostgreSQL, 35 Bytes

So einfach funktioniert PostgreSQL:

SELECT * FROM generate_series(1,5)

Wenn Sie es mit dem Namen brauchen:

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

Sie können dies auch mit Zeitstempeln tun. https://www.postgresql.org/docs/9.5/static/functions-srf.html

Riking
quelle
2
Ich schätze, ich ziehe nach Postgres
Dimgold
4

Gute Option von diesem Beitrag (gefunden von @Arnauld):

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

Für mich - es löst so ziemlich die Herausforderung.

Dimgold
quelle
Dies scheint darauf zu beruhen, dass in einer vorhandenen Tabelle bereits ein idFeld mit sehr großen Werten vorhanden ist. So hübsch datenbankspezifisch, und Sie könnten eine Zeile verpassen, wenn beispielsweise jemand die Produkt-ID = 4021 gelöscht hat.
BradC
Ja, aber es ist wirklich gut für relativ kleine Bereiche (1-7 für Tage, 1-12 für Monate usw. ...)
Dimgold
4

PostgreSQL-spezifisch

generate_series()generiert eine Menge, sodass Sie sie nicht nur in fromKlausel, sondern überall dort verwenden können, wo eine Menge auftreten kann:

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

Sie können auch direkt am Gerät arbeiten:

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

Wenn mehrere Sätze die gleiche Länge haben, können Sie sie parallel durchlaufen:

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

Für Sets mit unterschiedlichen Längen wird ein kartesisches Produkt erzeugt:

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)

Wenn Sie sie jedoch in der fromKlausel verwenden, erhalten Sie das kartesische Produkt auch für Sätze gleicher Länge:

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)

Es kann auch eine Reihe von Zeitstempeln erzeugen. Sie sind zum Beispiel am 30.06.2000 geboren und möchten wissen, in welchen Jahren Sie an einem Wochenende Geburtstag gefeiert haben:

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)
Mann bei der Arbeit
quelle
3

MS SQL hat eine undokumentierte Systemtabelle in der masterDatenbank aufgerufen spt_values. Es enthält unter anderem einen Zahlenbereich von 0 bis 2047:

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

Nützlich als Nummerntabelle, aber in einem CTE können Sie ziemlich schnell einige große Nummern erhalten:

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

(Diese funktionieren in MS-SQL, nicht sicher, ob sie für mySQL oder andere Plattformen funktionieren.)

Verwenden Sie für kleinere Mengen (bestellt oder nicht bestellt) den VALUESKonstruktor:

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

(Dies funktioniert für alles, obwohl Zeichenfolgen mit all den wiederholten einfachen Anführungszeichen ziemlich lang werden können.)

Dann können Sie mit einem benannten CTE (Common Table Expression) eine Kreuzmultiplikation durchführen, damit Sie ihn nicht wiederholen müssen:

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

Es gibt Unmengen anderer Techniken, suchen Sie nach "SQL, das eine Zahlentabelle generiert", obwohl die meisten nicht für das Golfen optimiert sind.

BradC
quelle
1
Würde das mit einem arbeiten limit Y, um beliebige Bereiche zu machen?
Rod
1
@ Rod In MS-SQL müssten Sie verwendenSELECT TOP 250 ...
BradC
Oh, ich habe den MSSQL-Header nicht gesehen = X
Rod
Funktioniert nicht mit MySQL, ist aber trotzdem nützlich :)
Dimgold
2

Eine weitere Option, diese speziell für MS SQL 2016 und höher:

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

Ich finde das wahrscheinlich praktischer für Listen von Zeichenfolgen, aber ich kann sehen, wie es auch bei Zahlen nützlich sein wird.

BradC
quelle
2

T-SQL, 98 Bytes

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
  • ✓ Einzelaussage
  • ✓ keine Verfahren
  • ✓ keine Variablen
  • ✓ Keine DDL-Anweisungen
  • ✓ Nur DQL-Anweisungen
Aplato
quelle
Dies ist eine nette, ordentliche T-SQL-Version von langelgjms Antwort . Auch die Exponenten sind ein guter Trick.
BradC,
1

Ein weiteres für 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
quelle