Sélection de plages de numéros SQL

19

J'ai trouvé assez difficile d'obtenir une plage de nombres sous forme de lignes MySQL .

Par exemple, la plage 1-5 est atteinte par:

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

aura pour résultat:

1
2
3
4
5

pour 0-99 je peux croiser joindre deux tables 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

J'en ai marre d'écrire tout ça UNION s et de chercher un moyen de réduire le code.

Des idées sur la façon de jouer au golf (par exemple une plage de 0 à 1 000 000) dans MySQL ou toute syntaxe SQL?

Des points supplémentaires sont accordés pour:

  • déclaration unique
  • pas de procédures
  • pas de variables
  • aucune instruction DDL
  • uniquement les instructions DQL
Dimgold
la source
2
Je ne sais pas si cela appartient à meta, ou à dba.stackexchange.com ou peut-être dans les conseils pour Golfing in SQL thread .
BradC
8
Fermer les électeurs: il s'agit d'un défi sur le sujet; les questions qui ne sont pas des défis liés au code de golf sont considérées comme des questions sur les astuces.
HyperNeutrino
3
J'aime un peu cette réponse de SO . Hackish au mieux, mais après tout, vous avez demandé une solution de golf.
Arnauld
@Arnauld c'est incroyable!
Dimgold
2
Si «tout SQL» inclut PostgreSQL, voir generate_series(). Nous avons ici quelques exemples d'utilisation .
manatwork

Réponses:

9

Pour les dialectes SQL qui prennent en charge les CTE récursifs comme sqlite, vous pouvez faire quelque chose comme ceci:

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

Cela ne dépend d'aucune table existante et vous pouvez modifier la clause LIMIT comme vous le souhaitez. J'ai vu à l'origine une variante de cela sur StackOverflow.

langelgjm
la source
2
Excellent. Voici une version golfée qui fonctionne dans MS SQL: WITH t AS(SELECT 1n UNION ALL SELECT n+1FROM t WHERE n<36)SELECT n FROM t Pour différents points de terminaison, changez simplement le 1et 36en ce que vous voulez.
BradC
1
Oups, si vous voulez plus de 100 lignes dans MS SQL, vous devrez peut-être ajouter option (maxrecursion 0)à la fin de ma déclaration ci-dessus, sinon il commet une erreur de récursivité sur 100. (Soit défini maxrecursionsur une valeur spécifique, soit sur 0 pour autoriser l'infini) .
BradC
6

Similaire à la méthode de @ BradC .

J'ai utilisé MS SQL, qui a une table [master]avec une plage de nombres de -1 à 2048. Vous pouvez utiliser l' BETWEENopérateur pour créer votre plage.

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

Si vous voulez jouer au golf, vous pouvez faire:

SELECT TOP 5 ROW_NUMBER()OVER(ORDER BY number)FROM master..spt_values
Oliver
la source
1
Pour le golf, vous économisez 2 octets avecWHERE number>0AND number<21
BradC
Pourquoi utilisez-vous distinct? Semble redondant.
Magic Octopus Urn
1
@MagicOctopusUrn Parce qu'il y a des numéros en double dans cette table.
Oliver
1
Oui, vous devez soit utiliser DISTINCT, soit utiliser WHERE type = 'P'. Distinct est légèrement plus court.
BradC
1
@BradC, ouSELECT DISTINCT(number+2)... WHERE number<19
Peter Taylor
4

Grande option de ce post (trouvé par @Arnauld):

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

Pour moi, cela résout à peu près le défi.

Dimgold
la source
Cela semble reposer sur une table existante ayant déjà un idchamp rempli de très grandes valeurs. Tellement spécifique à la base de données, et vous pourriez manquer une ligne si, par exemple, quelqu'un supprimait l'ID de produit = 4021.
BradC
Oui, mais c'est vraiment bon pour des gammes relativement petites (1-7 pour les jours, 1-12 pour les mois, etc.)
Dimgold
4

Spécifique à PostgreSQL

generate_series()génère un ensemble, vous pouvez donc l'utiliser non seulement dans la fromclause, mais partout où un ensemble peut se produire:

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

Vous pouvez également effectuer des opérations directement sur l'ensemble:

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

Si plusieurs ensembles ont la même longueur, vous pouvez les parcourir en parallèle:

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

Pour les ensembles de longueurs différentes, un produit cartésien est généré:

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)

Mais si vous les utilisez dans la fromclause, vous obtenez également un produit cartésien pour les mêmes ensembles de longueurs:

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)

Il peut également générer un ensemble d'horodatages. Par exemple, vous êtes né le 30/06/2000 et vous voulez savoir en quelles années vous avez célébré votre anniversaire un week-end:

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)
homme au travail
la source
3

MS SQL a une table système non documentée dans la masterbase de données appelée spt_values. Entre autres, il contient une plage de nombres de 0 à 2047:

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

Utile en tant que tableau de nombres juste en soi, mais dans un CTE, vous pouvez obtenir des gros nombres assez rapidement:

--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
la source
3

(Ceux-ci fonctionnent dans MS-SQL, je ne sais pas s'ils fonctionnent pour mySQL ou d'autres plates-formes.)

Pour les petits ensembles (ordonnés ou non ordonnés), utilisez le VALUESconstructeur:

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

(Cela fonctionne pour n'importe quoi, bien que les chaînes puissent devenir assez longues avec toutes les guillemets simples répétés.)

Ensuite, vous pouvez multiplier en utilisant un CTE nommé (expression de table commune) afin de ne pas avoir à le répéter:

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

Il existe de nombreuses autres techniques, recherchez «Génération SQL d'une table numérique», bien que la plupart ne soient pas optimisées pour le golf.

BradC
la source
1
Est-ce que cela fonctionnerait avec un limit Ypour créer des plages arbitraires?
Rod
1
@Rod Dans MS-SQL, vous devez utiliserSELECT TOP 250 ...
BradC
Oh, je n'ai pas vu l'en-tête MSSQL = X
Rod
ne fonctionne pas sur MySQL, mais est toujours utile :)
Dimgold
2

Encore une option, celle-ci spécifique à MS SQL 2016 et au-dessus:

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

Je trouverai probablement cela plus pratique pour les listes de chaînes, mais je peux voir comment cela sera également utile avec les nombres.

BradC
la source
2

T-SQL, 98 octets

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
  • ✓ déclaration unique
  • ✓ aucune procédure
  • ✓ aucune variable
  • ✓ aucune instruction DDL
  • ✓ uniquement les instructions DQL
Aplato
la source
Ceci est une belle version T-SQL bien rangée de la réponse de langelgjm . Les exposants sont également une astuce.
BradC
1

Un autre pour 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
la source