Intervalos de número de seleção SQL

19

Achei bastante difícil obter um intervalo de números como linhas MySQL.

Por exemplo, o intervalo de 1 a 5 é alcançado por:

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

vai resultar em:

1
2
3
4
5

para 0-99 eu posso cruzar duas tabelas 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

Estou cansado de escrever tudo isso UNIONe procurar uma maneira de reduzir o código.

Alguma idéia de como jogar golfe (por exemplo, faixa de 0 a 1.000.000) no MySQL ou em qualquer sintaxe SQL?

Pontos extras são dados para:

  • declaração única
  • sem procedimentos
  • sem variáveis
  • sem instruções DDL
  • apenas instruções DQL
Dimgold
fonte
2
Não tenho certeza se isso pertence à meta, ou ao dba.stackexchange.com, ou talvez às dicas de golfe no segmento SQL .
BradC
8
Para fechar os eleitores: Este é um desafio no tópico; as perguntas que não são desafios relacionados ao código de golfe são consideradas perguntas sobre dicas sobre tópicos.
HyperNeutrino
3
Eu meio que gosto desta resposta da SO . Hackish na melhor das hipóteses, mas você pediu uma solução de golfe depois de tudo.
Arnauld
@ Arnauld isso é incrível!
Dimgold
2
Se "qualquer SQL" inclui o PostgreSQL, consulte generate_series(). Temos alguns exemplos de uso aqui.
manatwork

Respostas:

9

Para dialetos SQL compatíveis com CTEs recursivos como o sqlite, você pode fazer algo como o seguinte:

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

Isso não depende de nenhuma tabela existente e você pode alterar a cláusula LIMIT conforme desejado. Originalmente, vi uma variante disso no StackOverflow.

langelgjm
fonte
2
Excelente. Aqui está uma versão golfed que funciona em MS SQL: WITH t AS(SELECT 1n UNION ALL SELECT n+1FROM t WHERE n<36)SELECT n FROM t Para diferentes pontos finais, basta alterar o 1e 36para o que quiser.
BradC
11
Ops, se você deseja mais de 100 linhas no MS SQL, pode ser necessário adicionar option (maxrecursion 0)ao final da minha instrução acima, caso contrário, ela gera recursões acima de 100. (Defina maxrecursionum valor específico ou 0 para permitir infinito) .
BradC
6

Semelhante ao método do @ BradC .

Usei o MS SQL, que possui uma tabela [master]com um intervalo numérico de -1 a 2048. Você pode usar o BETWEENoperador para criar seu intervalo.

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

Se você quiser jogar isso, você pode:

SELECT TOP 5 ROW_NUMBER()OVER(ORDER BY number)FROM master..spt_values
Oliver
fonte
11
Para jogar golfe, você economiza 2 bytes comWHERE number>0AND number<21
BradC
Por que você usa distintas? Parece redundante.
Magic Octopus Urn
11
@MagicOctopusUrn Porque existem números duplicados nessa tabela.
Oliver
11
Sim, você precisa usar DISTINCT ou WHERE type = 'P'. Distinto é um pouco mais curto.
BradC
11
@BradC, ouSELECT DISTINCT(number+2)... WHERE number<19
Peter Taylor
5

PostgreSQL, 35 bytes

O PostgreSQL é fácil:

SELECT * FROM generate_series(1,5)

Se você precisar nomeado:

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

Você também pode fazer isso com timestamps. https://www.postgresql.org/docs/9.5/static/functions-srf.html

Riking
fonte
2
Eu acho que eu estou movendo para postgres
Dimgold
4

Ótima opção deste post (encontrado por @Arnauld):

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

Para mim - praticamente resolve o desafio.

Dimgold
fonte
Isso parece depender de uma tabela existente que já possui um idcampo preenchido por valores muito grandes. Então banco de dados específico bonita, e você pode perder uma fila se, digamos, alguém excluídos ID do produto = 4021.
BradC
Sim, mas é realmente bom para intervalos relativamente pequenos (1-7 por dias, 1-12 por meses, etc.) #
Dimgold
4

Específico do PostgreSQL

generate_series()gera um conjunto, para que você possa usá-lo não apenas na fromcláusula, mas em qualquer lugar em que um conjunto possa ocorrer:

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

Você também pode executar operações diretamente no aparelho:

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

Se vários conjuntos tiverem o mesmo comprimento, você poderá atravessá-los em 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 com comprimentos diferentes, é gerado um produto 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)

Mas se você usá-los na fromcláusula, também obtém produtos cartesianos para os mesmos conjuntos de comprimento:

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)

Também pode gerar um conjunto de carimbos de data / hora. Por exemplo, você nasceu em 30/06/2000 e deseja saber em que anos comemorou seu aniversário em um fim 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)
homem a trabalhar
fonte
3

O MS SQL possui uma tabela de sistema não documentada no masterbanco de dados chamada spt_values. Entre outras coisas, ele contém um intervalo de números de 0 a 2047:

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

Útil como uma tabela de números por si só, mas em um CTE você pode obter grandes números rapidamente:

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

(Eles funcionam no MS-SQL, não tenho certeza se eles funcionam para o mySQL ou outras plataformas.)

Para conjuntos menores (ordenados ou não), use o VALUESconstrutor:

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

(Isso funciona para qualquer coisa, embora as strings possam ficar muito longas com todas as aspas simples repetidas.)

Em seguida, você pode multiplicar usando uma CTE (expressão comum da tabela) nomeada para não precisar repeti-la:

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

Existem inúmeras outras técnicas por aí, procure por "SQL gerando uma tabela numérica", embora a maioria não seja otimizada para jogar golfe.

BradC
fonte
11
Isso funcionaria com a limit Ypara fazer intervalos arbitrários?
Rod
11
@Rod Em MS-SQL, você teria que usarSELECT TOP 250 ...
BradC
Ah, eu não vi o cabeçalho do MSSQL = X
Rod
não funciona no MySQL, mas ainda é útil :)
Dimgold
2

Mais uma opção, essa específica para o MS SQL 2016 e superior:

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

Provavelmente vou achar isso mais útil para listas de cadeias de caracteres, mas também posso ver como será útil com números.

BradC
fonte
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
  • ✓ declaração única
  • ✓ sem procedimentos
  • ✓ sem variáveis
  • ✓ sem instruções DDL
  • ✓ apenas instruções DQL
Aplato
fonte
Esta é uma versão T-SQL bem organizada da resposta de langelgjm . Os expoentes também são um truque legal.
BradC 13/09
1

Outro para o 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
fonte