SQL selecciona rangos de números

19

Me resultó bastante difícil lograr un rango de números en filas MySQL.

Por ejemplo, el rango 1-5 se logra mediante:

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

resultará en:

1
2
3
4
5

para 0-99 puedo unir dos tablas de 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

Estoy cansado de escribir todos estos UNIONcorreos electrónicos y buscar una forma de reducir el código.

¿Alguna idea de cómo jugar golf (por ejemplo, rango de 0-1,000,000) en MySQL o cualquier sintaxis SQL?

Se otorgan puntos adicionales por:

  • declaración única
  • sin procedimientos
  • sin variables
  • sin declaraciones DDL
  • solo sentencias DQL
Dimgold
fuente
2
No estoy seguro si esto pertenece en meta, o en dba.stackexchange.com o tal vez en los consejos para jugar golf en el hilo SQL .
BradC
8
Para cerrar los votantes: este es un desafío sobre el tema; Las preguntas que no son desafíos relacionados con el código de golf se consideran preguntas de consejos sobre el tema.
HyperNeutrino
3
Me gusta esta respuesta de SO . Hackish en el mejor de los casos, pero pediste una solución de golf después de todo.
Arnauld
@Arnauld eso es increíble!
Dimgold
2
Si "cualquier SQL" incluye PostgreSQL, vea generate_series(). Tenemos un par de ejemplos de uso. aquí.
manatwork

Respuestas:

9

Para dialectos de SQL que admiten CTE recursivos como sqlite, puede hacer algo como lo siguiente:

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

Esto no depende de ninguna tabla existente y puede cambiar la cláusula LIMIT como desee. Originalmente vi una variante de esto en StackOverflow.

langelgjm
fuente
2
Excelente. Aquí hay una versión de golf que funciona en MS SQL: WITH t AS(SELECT 1n UNION ALL SELECT n+1FROM t WHERE n<36)SELECT n FROM t para diferentes puntos finales, simplemente cambie el 1y 36a lo que desee.
BradC
1
Vaya, si desea más de 100 filas en MS SQL, es posible que deba agregar option (maxrecursion 0)al final de mi declaración anterior, de lo contrario, se produce un error de recursión superior a 100. (Establezca maxrecursionun valor específico o 0 para permitir infinito) .
BradC
6

Similar al método de @ BradC .

Usé MS SQL, que tiene una tabla [master]con un rango de números de -1 a 2048. Puede usar el BETWEENoperador para crear su rango.

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

Si quieres jugar al golf, puedes hacer:

SELECT TOP 5 ROW_NUMBER()OVER(ORDER BY number)FROM master..spt_values
Oliver
fuente
1
Para jugar golf ahorra 2 bytes conWHERE number>0AND number<21
BradC
¿Por qué usas distinto? Parece redundante
Magic Octopus Urn
1
@MagicOctopusUrn Porque hay números duplicados en esa tabla.
Oliver
1
Sí, necesita usar DISTINCT o usar WHERE type = 'P'. El distintivo es ligeramente más corto.
BradC
1
@BradC, oSELECT DISTINCT(number+2)... WHERE number<19
Peter Taylor
4

Gran opción de esta publicación (encontrada por @Arnauld):

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

Para mí, prácticamente resuelve el desafío.

Dimgold
fuente
Esto parece depender de una tabla existente que ya tiene un idcampo poblado a través de valores muy grandes. Tan bonita base de datos específica, y podría perderse una fila si, por ejemplo, alguien elimina el ID del producto = 4021.
BradC
Sí, pero es realmente bueno para rangos relativamente pequeños (1-7 por días, 1-12 por meses, etc.)
Dimgold
4

PostgreSQL específico

generate_series()genera un conjunto, por lo que puede usarlo no solo en la fromcláusula, sino en cualquier lugar donde pueda ocurrir un conjunto:

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

También puede realizar operaciones directamente en el conjunto:

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

Si varios conjuntos tienen la misma longitud, puede atravesarlos en paralelo:

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

Para conjuntos con diferentes longitudes se genera un producto cartesiano:

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)

Pero si los usa en la fromcláusula, también obtiene el producto cartesiano para conjuntos de la misma longitud:

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)

También puede generar un conjunto de marcas de tiempo. Por ejemplo, usted nació el 30-06-2002 y quiere saber en qué años celebró su cumpleaños en un fin de semana:

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)
hombre trabajando
fuente
3

MS SQL tiene una tabla de sistema no documentada en la masterbase de datos llamada spt_values. Entre otras cosas, contiene un rango de números del 0 al 2047:

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

Útil como tabla de números por sí sola, pero en un CTE puede obtener algunos números grandes con bastante rapidez:

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

(Funcionan en MS-SQL, no estoy seguro si funcionan para mySQL u otras plataformas).

Para conjuntos más pequeños (ordenados o no ordenados), use el VALUESconstructor:

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

(Esto funciona para cualquier cosa, aunque las cadenas pueden ser bastante largas con todas las comillas simples repetidas).

Luego puede realizar una multiplicación cruzada utilizando un CTE con nombre (expresión de tabla común) para que no tenga que repetirlo:

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

Existen muchas otras técnicas, busque "SQL que genera una tabla de números", aunque la mayoría no están optimizadas para jugar al golf.

BradC
fuente
1
¿Funcionaría esto con a limit Ypara hacer rangos arbitrarios?
Rod
1
@Rod En MS-SQL tendrías que usarSELECT TOP 250 ...
BradC
Oh, no vi el encabezado MSSQL = X
Rod
no funciona en MySQL, pero sigue siendo útil :)
Dimgold
2

Una opción más, esta específica para MS SQL 2016 y superior:

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

Probablemente encuentre esto más útil para las listas de cadenas, pero puedo ver formas en que también será útil con números.

BradC
fuente
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
  • ✓ declaración única
  • ✓ sin procedimientos
  • ✓ sin variables
  • ✓ sin declaraciones DDL
  • ✓ solo declaraciones DQL
Aplato
fuente
Esta es una buena versión ordenada de T-SQL de la respuesta de langelgjm . Los exponentes también son un buen truco.
BradC
1

Otro para 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
fuente