Генерировать целочисленную последовательность в MySQL

Мне нужно сделать соединение с таблицей / набор результатов / все, что имеет целые числа от n до m включительно. Есть ли тривиальный способ получить это, не просто создавая стол?

(Кстати, как будет называться этот тип конструкции, "мета-запрос"?)

Mn ограничен чем-то разумным ( < 1000)

19 ответов

Решение

В MySQL нет генератора порядковых номеров ( CREATE SEQUENCE). Ближайшая вещь AUTO_INCREMENT, который может помочь вам построить таблицу.

Я нашел это решение в Интернете

SELECT @row := @row + 1 as row, t.*
FROM some_table t, (SELECT @row := 0) r

Один запрос, быстрый и выполняет именно то, что я хотел: теперь я могу "нумеровать" "выборки", найденные в сложном запросе, с уникальными числами, начинающимися с 1 и увеличивающимися один раз для каждой строки в результате.

Я думаю, что это также будет работать для проблемы, указанной выше: отрегулируйте начальное начальное значение для @row и добавьте предельное предложение, чтобы установить максимум.

Кстати: я думаю, что "р" действительно не нужно.

DDSP

Если вы используете MyDB форк MariaDB, то SEQUENCE Движок позволяет напрямую генерировать числовые последовательности. Это делается с помощью виртуальных (поддельных) таблиц с одним столбцом.

Например, чтобы сгенерировать последовательность целых чисел от 1 до 1000, сделайте это

     SELECT seq FROM seq_1_to_1000;

Для 0 до 11, сделайте это.

     SELECT seq FROM seq_0_to_11;

Сделайте это, чтобы получить значения DATE на неделю, начиная с сегодняшнего дня.

SELECT FROM_DAYS(seq + TO_DAYS(CURDATE)) dateseq FROM seq_0_to_6

За десятилетие подряд DATE значения, начинающиеся с '2010-01-01', делают это.

SELECT FROM_DAYS(seq + TO_DAYS('2010-01-01')) dateseq
  FROM seq_0_to_3800
 WHERE FROM_DAYS(seq + TO_DAYS('2010-01-01')) < '2010-01-01' + INTERVAL 10 YEAR

Если вы не используете MariaDB, пожалуйста, подумайте об этом.

Следующий вернет 1..10000 и не так медленно

SELECT @row := @row + 1 as row FROM 
(select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) t,
(select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) t2, 
(select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) t3, 
(select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) t4, 
(SELECT @row:=0)

Попробуйте это.. у меня работает в MySQL версии 8.0. Вы можете изменить ниже запрос в соответствии с вашим необходимым диапазоном

WITH recursive numbers AS (
    select 0 as Date
   union all
   select Date + 1
   from numbers
   where Date < 10)
select * from numbers;

и да без создания таблицы, как указано в вашем посте

Последовательность чисел от 1 до 100.000:

SELECT e*10000+d*1000+c*100+b*10+a n FROM
(select 0 a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) t1,
(select 0 b union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) t2,
(select 0 c union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) t3,
(select 0 d union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) t4,
(select 0 e union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) t5
order by 1

Я использую его для проверки, если какое-то число не в порядке, что-то вроде этого:

select * from (
    select 121 id
    union all select 123
    union all select 125
    union all select 126
    union all select 127
    union all select 128
    union all select 129
) a
right join (
    SELECT e*10000+d*1000+c*100+b*10+a n FROM
    (select 0 a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) t1,
    (select 0 b union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) t2,
    (select 0 c union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) t3,
    (select 0 d union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) t4,
    (select 0 e union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) t5
    order by 1
) seq on seq.n=a.id
where seq.n between 121 and 129
and   id is null

Результатом будет разрыв числа 122 и 124 последовательности между 121 и 129:

id     n
----   ---
null   122
null   124

Может быть, это кому-то поможет!

Самый простой способ сделать это:

SET @seq := 0;
SELECT @seq := FLOOR(@seq + 1) AS sequence, yt.*
FROM your_table yt;

или в одном запросе:

SELECT @seq := FLOOR(@seq + 1) AS sequence, yt.*
FROM (SELECT @seq := 0) s, your_table yt;

FLOOR() функция используется здесь, чтобы получить INTEGER вместо FLOAT, Иногда это необходимо.

Мой ответ был вдохновлен ответом Дэвида Бедного. Спасибо Дэвид!

Счетчик от 1 до 1000:

  • нет необходимости создавать стол
  • время выполнения ~ 0,0014 сек
  • может быть преобразован в вид

:

select tt.row from
(
SELECT cast( concat(t.0,t2.0,t3.0,t4.0) + 1 As UNSIGNED) as 'row' FROM 
(select 0 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) t,
(select 0 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) t2, 
(select 0 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) t3
) tt
order by tt.row

Кредиты: ответ, комментарий Сета МакКоли под ответом.

Вы можете попробовать что-то вроде этого:

SELECT @rn:=@rn+1 as n
FROM (select @rn:=2)t, `order` rows_1, `order` rows_2 --, rows_n as needed...
LIMIT 4

куда order Это просто пример таблицы с достаточно большим набором строк.

Редактировать: оригинальный ответ был неправильным, и любой кредит должен идти к Дэвиду Пур, который предоставил рабочий пример той же концепции

Есть способ получить диапазон значений в одном запросе, но он немного медленный. Это можно ускорить с помощью кеш-таблиц.

Предположим, вы хотите выбрать с диапазоном всех значений BOOLEAN:

SELECT 0 as b UNION SELECT 1 as b;

мы можем сделать вид

CREATE VIEW ViewBoolean AS SELECT 0 as b UNION SELECT 1 as b;

тогда вы можете сделать байт

CREATE VIEW ViewByteValues AS
SELECT b0.b + b1.b*2 + b2.b*4 + b3.b*8 + b4.b*16 + b5.b*32 + b6.b*64 + b7.b*128 as v FROM
ViewBoolean b0,ViewBoolean b1,ViewBoolean b2,ViewBoolean b3,ViewBoolean b4,ViewBoolean b5,ViewBoolean b6,ViewBoolean b7;

тогда вы можете сделать

CREATE VIEW ViewInt16 AS
SELECT b0.v + b1.v*256 as v FROM
ViewByteValues b0,ViewByteValues b1;

тогда вы можете сделать

SELECT v+MIN as x FROM ViewInt16 WHERE v<MAX-MIN;

Чтобы ускорить это, я пропустил автоматический расчет значений байтов и сделал себе

CREATE VIEW ViewByteValues AS
SELECT 0 as v UNION SELECT 1 as v UNION SELECT ...
...
...254 as v UNION SELECT 255 as v;

Если вам нужен диапазон дат, вы можете сделать.

SELECT DATE_ADD('start_date',v) as day FROM ViewInt16 WHERE v<NumDays;

или же

SELECT DATE_ADD('start_date',v) as day FROM ViewInt16 WHERE day<'end_date';

Вы могли бы ускорить это с немного более быстрой функцией MAKEDATE

SELECT MAKEDATE(start_year,1+v) as day FRON ViewInt16 WHERE day>'start_date' AND day<'end_date';

Обратите внимание, что эти трюки ОЧЕНЬ МЕДЛЕННЫЕ и позволяют создавать только последовательности FINITE в заранее определенной области (например, int16 = 0...65536)

Я уверен, что вы можете немного изменить запросы, чтобы ускорить процесс, указав MySQL, где прекратить вычислять;) (используя предложения ON вместо предложений WHERE и тому подобное)

Например:

SELECT MIN + (b0.v + b1.v*256 + b2.v*65536 + b3.v*16777216) FROM
ViewByteValues b0,
ViewByteValues b1,
ViewByteValues b2,
ViewByteValues b3
WHERE (b0.v + b1.v*256 + b2.v*65536 + b3.v*16777216) < MAX-MIN;

будет занят ваш сервер SQL в течение нескольких часов

тем не мение

SELECT MIN + (b0.v + b1.v*256 + b2.v*65536 + b3.v*16777216) FROM
ViewByteValues b0
INNER JOIN ViewByteValues b1 ON (b1.v*256<(MAX-MIN))
INNER JOIN ViewByteValues b2 ON (b2.v*65536<(MAX-MIN))
INNER JOIN ViewByteValues b3 ON (b3.v*16777216<(MAX-MIN)
WHERE (b0.v + b1.v*256 + b2.v*65536 + b3.v*16777216) < (MAX-MIN);

будет работать достаточно быстро - даже если MAX-MIN огромен, если вы ограничите результат с помощью LIMIT 1,30 или чего-то еще. COUNT(*), однако, займет много времени, и если вы допустите ошибку, добавив ORDER BY, когда MAX-MIN больше, чем, скажем, 100k, для расчета снова потребуется несколько секунд...

Похоже, вы можете создавать достаточно большие наборы с:

select 9 union all select 10 union all select 11 union all select 12 union all select 13 ...

Я получил переполнение стека парсера в 5300-х, на 5.0.51a.

Вот компактная двоичная версия метода, используемого в других ответах здесь:

select ((((((b7.0 << 1 | b6.0) << 1 | b5.0) << 1 | b4.0) 
                  << 1 | b3.0) << 1 | b2.0) << 1 | b1.0) << 1 | b0.0 as n
from (select 0 union all select 1) as b0,
     (select 0 union all select 1) as b1,
     (select 0 union all select 1) as b2,
     (select 0 union all select 1) as b3,
     (select 0 union all select 1) as b4,
     (select 0 union all select 1) as b5,
     (select 0 union all select 1) as b6,
     (select 0 union all select 1) as b7

Нет никаких уникальных или сортирующих фаз, нет преобразования строки в число, нет арифметических операций, и каждая фиктивная таблица имеет только 2 строки, поэтому она должна быть довольно быстрой.

Эта версия использует 8 "битов", поэтому она имеет значение от 0 до 255, но вы можете легко настроить это.

Этот запрос генерирует числа от 0 до 1023. Я считаю, что он будет работать в любом варианте базы данных SQL:

select
     i0.i
    +i1.i*2
    +i2.i*4
    +i3.i*8
    +i4.i*16
    +i5.i*32
    +i6.i*64
    +i7.i*128
    +i8.i*256
    +i9.i*512
    as i
from
               (select 0 as i union select 1) as i0
    cross join (select 0 as i union select 1) as i1
    cross join (select 0 as i union select 1) as i2
    cross join (select 0 as i union select 1) as i3
    cross join (select 0 as i union select 1) as i4
    cross join (select 0 as i union select 1) as i5
    cross join (select 0 as i union select 1) as i6
    cross join (select 0 as i union select 1) as i7
    cross join (select 0 as i union select 1) as i8
    cross join (select 0 as i union select 1) as i9

Насколько большой м?

Вы могли бы сделать что-то вроде:

create table two select null foo union all select null;
create temporary table seq ( foo int primary key auto_increment ) auto_increment=9 select a.foo from two a, two b, two c, two d;
select * from seq where foo <= 23;

где auto_increment имеет значение n, а предложение where сравнивается с m, и количество повторений двух таблиц составляет по меньшей мере ceil (log (m-n + 1) / log (2)).

(Невременные две таблицы можно было бы опустить, заменив две на (выберите null, foo union all выберите null) в последовательности создания временной таблицы.)

Начиная с Mariadb 10.2 (и MySQL теперь тоже имеет его), вы можете сделать это очень элегантно с помощью Common Table Expression (CTE).

Вот так:

      with recursive numbers (n) as (
  select 1 as n
  union
  select n+1 from numbers where n<100
)
select * from numbers

Конечно, теперь вы можете присоединиться к этомуnumbersCTE со своими таблицами, и у вас есть 1..n (или n..m по тому же принципу).

Предупреждение: если вы вставляете числа по одной строке за раз, вы в конечном итоге будете выполнять N команд, где N - это количество строк, которое нужно вставить.

Вы можете получить это значение до O(log N), используя временную таблицу (для вставки чисел от 10000 до 10699 см. Ниже):

mysql> CREATE TABLE `tmp_keys` (`k` INTEGER UNSIGNED, PRIMARY KEY (`k`));
Query OK, 0 rows affected (0.11 sec)

mysql> INSERT INTO `tmp_keys` VALUES (0),(1),(2),(3),(4),(5),(6),(7);
Query OK, 8 rows affected (0.03 sec)
Records: 8  Duplicates: 0  Warnings: 0

mysql> INSERT INTO `tmp_keys` SELECT k+8 from `tmp_keys`;
Query OK, 8 rows affected (0.02 sec)
Records: 8  Duplicates: 0  Warnings: 0

mysql> INSERT INTO `tmp_keys` SELECT k+16 from `tmp_keys`;
Query OK, 16 rows affected (0.03 sec)
Records: 16  Duplicates: 0  Warnings: 0

mysql> INSERT INTO `tmp_keys` SELECT k+32 from `tmp_keys`;
Query OK, 32 rows affected (0.03 sec)
Records: 32  Duplicates: 0  Warnings: 0

mysql> INSERT INTO `tmp_keys` SELECT k+64 from `tmp_keys`;
Query OK, 64 rows affected (0.03 sec)
Records: 64  Duplicates: 0  Warnings: 0

mysql> INSERT INTO `tmp_keys` SELECT k+128 from `tmp_keys`;
Query OK, 128 rows affected (0.05 sec)
Records: 128  Duplicates: 0  Warnings: 0

mysql> INSERT INTO `tmp_keys` SELECT k+256 from `tmp_keys`;
Query OK, 256 rows affected (0.03 sec)
Records: 256  Duplicates: 0  Warnings: 0

mysql> INSERT INTO `tmp_keys` SELECT k+512 from `tmp_keys`;
Query OK, 512 rows affected (0.11 sec)
Records: 512  Duplicates: 0  Warnings: 0

mysql> INSERT INTO inttable SELECT k+10000 FROM `tmp_keys` WHERE k<700;
Query OK, 700 rows affected (0.16 sec)
Records: 700  Duplicates: 0  Warnings: 0

edit: fyi, к сожалению, это не будет работать с настоящей временной таблицей с MySQL 5.0, так как она не может вставляться в себя (вы можете прыгать назад и вперед между двумя временными таблицами).

редактировать: вы могли бы использовать механизм хранения MEMORY, чтобы предотвратить это на самом деле утечка "реальной" базы данных. Интересно, разработал ли кто-нибудь механизм виртуального хранилища NUMBERS для создания виртуального хранилища для создания таких последовательностей, как этот. (увы, непортативный вне MySQL)

попробуйте следующее

      select
ROW_NUMBER() OVER (ORDER BY id asc) AS 'rowId', otherField from table;
      with t1 as (
select 0 union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9
)
SELECT ROW_NUMBER() over ()
FROM
t1,
t1 as t2;

Можно продолжить наложение таблицы t1 на любой размер таблицы (10 в n-й степени). А потом можно добавить лимит X, чтобы отключить его.

Если бы вы использовали Oracle, "конвейерные функции" были бы подходящим вариантом. К сожалению, MySQL не имеет такой конструкции.

В зависимости от масштаба чисел, наборы которых вы хотите, я вижу два простых способа: вы либо заполняете временную таблицу только теми числами, которые вам нужны (возможно, используя таблицы памяти, заполненные хранимой процедурой) для одного запроса, или, сначала вы строите большой стол, который насчитывает от 1 до 1 000 000, и выбираете его ограниченные области.

Другие вопросы по тегам