Вибір діапазонів номерів SQL


19

Мені було досить важко досягти діапазону чисел, як рядки MySQL.

Наприклад, діапазон 1-5 досягається за рахунок:

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

це призведе до:

1
2
3
4
5

для 0-99 я можу перехрестити приєднатися до двох таблиць 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

Я втомився писати всі ці UNIONі шукаю спосіб зменшити код.

Будь-які ідеї, як гольфувати його (наприклад, діапазон 0-1000000) у MySQL чи будь-якому синтаксисі SQL?

Додаткові бали надаються за:

  • єдине твердження
  • ніяких процедур
  • немає змінних
  • немає заяв DDL
  • лише оператори DQL

2
Не впевнений, чи належить це в мета, або в dba.stackexchange.com чи, можливо, в підказках щодо Гольфінгу в потоці SQL .
BradC

8
Закрити виборців: це актуальна проблема; Питання, які не є проблемами, пов’язаними з кодом для гольфу, розглядаються підказками на тему.
HyperNeutrino

3
Мені якось подобається ця відповідь . Хакіш у кращому випадку, але ти все-таки попросив рішення для гольфу.
Арнольд

@Arnauld це дивовижно!
Дімгольд

2
Якщо "будь-який SQL" включає PostgreSQL, див generate_series(). У нас є кілька прикладів використання тут.
манатура

Відповіді:


9

Для діалектів SQL, які підтримують рекурсивні CTE, такі як sqlite, ви можете зробити щось на зразок наступного:

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

Це не залежить від будь-якої існуючої таблиці, і ви можете змінити пункт LIMIT за бажанням. Я спочатку бачив варіант цього в StackOverflow.


2
Відмінно. Ось гольф-версія, яка працює в MS SQL: WITH t AS(SELECT 1n UNION ALL SELECT n+1FROM t WHERE n<36)SELECT n FROM t Для різних кінцевих точок просто змініть 1і 36на все, що вам потрібно.
BradC

1
На жаль, якщо вам потрібно більше 100 рядків у MS SQL, можливо, вам доведеться додати option (maxrecursion 0)до кінця мого вищевикладеного висловлювання, інакше воно буде помилковим для рекурсії понад 100. (Або встановлено maxrecursionна певне значення, або на 0, щоб дозволити нескінченність) .
BradC

6

Схожий на метод @ BradC .

Я використовував MS SQL, в якому є таблиця [master]з діапазоном чисел від -1 до 2048. Ви можете використовувати BETWEENоператор для створення діапазону.

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

Якщо ви хочете пограти в це поле, ви можете:

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

1
Для гри в гольф ви економите 2 байти за допомогоюWHERE number>0AND number<21
BradC

Чому ви використовуєте виразні? Здається зайвим.
Magic Octopus Urn

1
@MagicOctopusUrn Оскільки у цій таблиці є дублікати номерів.
Олівер

1
Так, вам або потрібно використовувати DISTINCT або використовувати WHERE type = 'P'. Відмінність трохи коротша.
BradC

1
@BradC, абоSELECT DISTINCT(number+2)... WHERE number<19
Пітер Тейлор


4

Відмінний варіант з цієї публікації (знайдений @Arnauld):

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

Для мене - це в значній мірі вирішує завдання.


Це, здається, спирається на існуючу таблицю, у якій вже є idполе, заповнене дуже великими значеннями. Тож досить
базова

Так, але це дуже добре для порівняно невеликих діапазонів (1-7 днів, 1-12 місяців тощо ...)
Dimgold

4

Специфічний для PostgreSQL

generate_series()створює набір, тож ви можете користуватися ним не тільки в fromпункті, але й у будь-якому місці, де набір може виникнути:

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

Ви також можете робити операції безпосередньо на наборі:

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

Якщо кілька наборів мають однакову довжину, ви можете пройти їх паралельно:

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

Для наборів різної довжини генерується декартовий виріб:

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)

Але якщо ви їх використовуєте в fromпункті, ви отримуєте декартовий продукт для тих же наборів довжини:

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)

Він також може генерувати набір часових позначок. Наприклад, ви народилися 2000-06-30 і хочете дізнатися, у яких роках ви відзначали день народження у вихідні:

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 має незадокументовану системну таблицю в masterбазі даних, що називається spt_values. Крім усього іншого, він містить діапазон чисел від 0 до 2047 року:

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

Корисна як таблиця цифр сама по собі, але в CTE ви можете отримати великі цифри досить швидко:

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

(Вони працюють у MS-SQL, не впевнені, чи працюють вони для mySQL чи інших платформ.)

Для менших наборів (упорядкованих чи не упорядкованих) використовуйте VALUESконструктор:

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

(Це працює для будь-чого, хоча рядки можуть бути досить довгими з усіма повтореними цитатами.)

Тоді ви можете перехрещувати множення, використовуючи названий CTE (загальний вираз таблиці), так що вам не доведеться повторювати це:

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

Є багато інших методик, шукайте "SQL, що генерує таблицю чисел", хоча більшість не оптимізована для гри в гольф.


1
Чи буде це працювати з a, limit Yщоб зробити довільні діапазони?
прут

1
@Rod У MS-SQL вам доведеться користуватисяSELECT TOP 250 ...
BradC

О, я не бачив заголовка MSSQL = X
Rod

не працює на MySQL, але все ще корисно :)
Dimgold

2

Ще один варіант, цей специфічний для MS SQL 2016 і вище:

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

Я, мабуть, знайду це більш зручним для списків рядків, але я можу побачити, як це буде корисно і для чисел.


2

T-SQL, 98 байт

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
  • ✓ одне твердження
  • ✓ жодних процедур
  • ✓ немає змінних
  • ✓ немає заяв DDL
  • ✓ лише оператори DQL

Це приємна охайна версія T-SQL відповіді langelgjm . Експоненти теж акуратний трюк.
BradC

1

Ще для 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;
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.