I'd like to generate a sequence in MariaDB starting at 1 and ending with whatever is the largest id in some other table. That other table has roughly this schema:
create table main (
id integer primary key,
-- ... other columns --
);
I've learned about MariaDB STORAGE engine for generating sequences but on the surface that's good only for generating static sequences, where you know the range at the time of writing the query, e.g.
MariaDB [skynetdb]> select * from seq_1_to_3;
+-----+
| seq |
+-----+
| 1 |
| 2 |
| 3 |
+-----+
3 rows in set (0.001 sec)
But how do I generate sequence whose upper bound is the result of select max(id) from main
? It seems that MariaDB doesn't accept meta-queries where I would construct a name of a table to query and then query it in a single query, e.g. something like this:
-- INVALID, FOR ILLUSTRATION ONLY
select * from concat('seq_1_to_', select max(id) from main)
Or is it possible? How? (Note I'm aware that I can do one additional roundtrip and construct the sequence table name on the client, but I want to avoid it because I don't have a feature rich client, only mysql
command)
The following recursive common table expression (CTE) works
MariaDB [skynetdb]> with recursive nums(n) as (select 1 union all select n+1 from nums where n <= (select max(id) from main)) select * from nums;
but it's too slow. It seems it constructs whole nums
table in memory first.
-
1You can use a prepared statement that concatenates strings first.Kondybas– Kondybas2020年03月19日 20:01:59 +00:00Commented Mar 19, 2020 at 20:01
1 Answer 1
SELECT seq
FROM seq_0_to_99999999
WHERE seq BETWEEN 12345678 and 12345688;
works fine and fast. It returned 11 rows.
A more complex query:
SELECT MIN(FROM_DAYS(seq)) AS first_day,
MAX(FROM_DAYS(seq)) AS last_day,
COUNT(*) AS number_of_days
FROM seq_0_to_99999999
WHERE seq BETWEEN TO_DAYS('2020-01-01')
AND TO_DAYS(CURDATE());
Returned:
+------------+------------+----------------+
| first_day | last_day | number_of_days |
+------------+------------+----------------+
| 2020年01月01日 | 2020年04月02日 | 93 |
+------------+------------+----------------+
1 row in set (0.00 sec)
It, too, was fast, and touched only 94 rows according to the 'Handler_read%' values. Cf: http://mysql.rjweb.org/doc.php/index_cookbook_mysql#handler_counts
For your case, you may need something like
WHERE seq BETWEEN ...
AND ( SELECT MAX(...) FROM ... )
I find that the general use of seq is to generate a generous number of values, then let WHERE
go for the range I need. The Optimizer avoids really generating the zillion values I seem to be asking for. (At least in examples like these.)