【发布时间】:2010-09-16 06:01:03
【问题描述】:
如何从 MySQL 查询中生成一系列连续数字(每行一个),以便我可以将它们插入到表中?
例如:
nr
1
2
3
4
5
我只想为此使用 MySQL(而不是 PHP 或其他语言)。
【问题讨论】:
-
您想将此添加到现有记录中还是添加到全新表中?
-
为什么不能使用 auto_increment 列?
如何从 MySQL 查询中生成一系列连续数字(每行一个),以便我可以将它们插入到表中?
例如:
nr
1
2
3
4
5
我只想为此使用 MySQL(而不是 PHP 或其他语言)。
【问题讨论】:
DECLARE i INT DEFAULT 0;
WHILE i < 6 DO
/* insert into table... */
SET i = i + 1;
END WHILE;
【讨论】:
如果您需要表中的记录并且想要避免并发问题,请按照以下步骤操作。
首先你创建一个表来存储你的记录
CREATE TABLE `incr` (
`Id` int(11) NOT NULL auto_increment,
PRIMARY KEY (`Id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
第二次创建这样的存储过程:
DELIMITER ;;
CREATE PROCEDURE dowhile()
BEGIN
DECLARE v1 INT DEFAULT 5;
WHILE v1 > 0 DO
INSERT incr VALUES (NULL);
SET v1 = v1 - 1;
END WHILE;
END;;
DELIMITER ;
最后调用 SP:
CALL dowhile();
SELECT * FROM incr;
结果
Id
1
2
3
4
5
【讨论】:
这是一种无需循环的基于集合的方法。这也可以制成视图以供重用。该示例显示了从 0 到 999 的序列的生成,但当然可以对其进行修改以适应。
INSERT INTO
myTable
(
nr
)
SELECT
SEQ.SeqValue
FROM
(
SELECT
(HUNDREDS.SeqValue + TENS.SeqValue + ONES.SeqValue) SeqValue
FROM
(
SELECT 0 SeqValue
UNION ALL
SELECT 1 SeqValue
UNION ALL
SELECT 2 SeqValue
UNION ALL
SELECT 3 SeqValue
UNION ALL
SELECT 4 SeqValue
UNION ALL
SELECT 5 SeqValue
UNION ALL
SELECT 6 SeqValue
UNION ALL
SELECT 7 SeqValue
UNION ALL
SELECT 8 SeqValue
UNION ALL
SELECT 9 SeqValue
) ONES
CROSS JOIN
(
SELECT 0 SeqValue
UNION ALL
SELECT 10 SeqValue
UNION ALL
SELECT 20 SeqValue
UNION ALL
SELECT 30 SeqValue
UNION ALL
SELECT 40 SeqValue
UNION ALL
SELECT 50 SeqValue
UNION ALL
SELECT 60 SeqValue
UNION ALL
SELECT 70 SeqValue
UNION ALL
SELECT 80 SeqValue
UNION ALL
SELECT 90 SeqValue
) TENS
CROSS JOIN
(
SELECT 0 SeqValue
UNION ALL
SELECT 100 SeqValue
UNION ALL
SELECT 200 SeqValue
UNION ALL
SELECT 300 SeqValue
UNION ALL
SELECT 400 SeqValue
UNION ALL
SELECT 500 SeqValue
UNION ALL
SELECT 600 SeqValue
UNION ALL
SELECT 700 SeqValue
UNION ALL
SELECT 800 SeqValue
UNION ALL
SELECT 900 SeqValue
) HUNDREDS
) SEQ
【讨论】:
这是硬件工程师版本的Pittsburgh DBA's solution:
SELECT
(TWO_1.SeqValue + TWO_2.SeqValue + TWO_4.SeqValue + TWO_8.SeqValue + TWO_16.SeqValue) SeqValue
FROM
(SELECT 0 SeqValue UNION ALL SELECT 1 SeqValue) TWO_1
CROSS JOIN (SELECT 0 SeqValue UNION ALL SELECT 2 SeqValue) TWO_2
CROSS JOIN (SELECT 0 SeqValue UNION ALL SELECT 4 SeqValue) TWO_4
CROSS JOIN (SELECT 0 SeqValue UNION ALL SELECT 8 SeqValue) TWO_8
CROSS JOIN (SELECT 0 SeqValue UNION ALL SELECT 16 SeqValue) TWO_16;
【讨论】:
假设您想在表格中插入数字 1 到 100。只要您有其他一些至少有那么多行的表(与表的内容无关),那么这是我的首选方法:
INSERT INTO pivot100
SELECT @ROW := @ROW + 1 AS ROW
FROM someOtherTable t
join (SELECT @ROW := 0) t2
LIMIT 100
;
想要一个以非 1 开头的范围?只需更改 @ROW 在联接中设置的内容即可。
【讨论】:
大家都明白,这是相当hacky,所以要小心使用
SELECT
id % 12 + 1 as one_to_twelve
FROM
any_large_table
GROUP BY
one_to_twelve
;
【讨论】:
我知道(在 MySQL 中)创建具有长序列的表的“最短”方法是将现有表与其自身(交叉)连接。由于任何(常见)MySQL 服务器都有 information_schema.COLUMNS 表,我会使用它:
DROP TABLE IF EXISTS seq;
CREATE TABLE seq (i MEDIUMINT AUTO_INCREMENT PRIMARY KEY)
SELECT NULL AS i
FROM information_schema.COLUMNS t1
JOIN information_schema.COLUMNS t2
JOIN information_schema.COLUMNS t3
LIMIT 100000; -- <- set your limit here
通常一个连接应该足以创建超过 100 万行 - 但多一个连接不会受到伤害 :-) - 只是不要忘记设置限制。
如果您想包含0,您应该“删除”AUTO_INCEMENT 属性。
ALTER TABLE seq ALTER i DROP DEFAULT;
ALTER TABLE seq MODIFY i MEDIUMINT;
现在你可以插入0
INSERT INTO seq (i) VALUES (0);
还有负数
INSERT INTO seq (i) SELECT -i FROM seq WHERE i <> 0;
您可以使用验证数字
SELECT MIN(i), MAX(i), COUNT(*) FROM seq;
【讨论】:
我想分享的想法不是对问题的准确回答,但可能对某些人有用,所以我想分享它。
如果您经常只需要一组有限的数字,那么创建一个包含您可能需要的数字的表格并每次都使用该表格会很有帮助。例如:
CREATE TABLE _numbers (num int);
INSERT _numbers VALUES (0), (1), (2), (3), ...;
这只能在您需要低于某个合理限制的数字时应用,因此不要将其用于生成序列 1...100 万,但可以用于例如 1...10k 的数字。
如果您在_numbers 表中有这个数字列表,那么您可以编写这样的查询,以获取字符串的各个字符:
SELECT number, substr(name, num, 1)
FROM users
JOIN _numbers ON num < length(name)
WHERE user_id = 1234
ORDER BY num;
如果您需要大于 10k 的数字,则可以将表连接到自身:
SELECT n1.num * 10000 + n2.num
FROM _numbers n1
JOIN _numbers n2
WHERE n1 < 100
ORDER BY n1.num * 10000 + n2.num; -- or just ORDER BY 1 meaning the first column
【讨论】:
与接受的响应非常相似,但使用 mysql >= 8.0 的新 WITH 语法,这使得更易读,意图也更清晰
WITH DIGITS (N) 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
UNITS.N + TENS.N*10 + HUNDREDS.N*100 + THOUSANDS.N*1000
FROM
DIGITS AS UNITS, DIGITS AS TENS, DIGITS AS HUNDREDS, DIGITS AS THOUSANDS;
【讨论】:
这是基于之前的答案 (https://stackoverflow.com/a/53125278/2009581),但与 MySQL 5.7 兼容。它适用于副本和只读用户:
SELECT x1.N + x10.N*10 + x100.N*100 + x1000.N*1000
FROM (SELECT 0 AS N 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) x1,
(SELECT 0 AS N 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) x10,
(SELECT 0 AS N 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) x100,
(SELECT 0 AS N 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) x1000
WHERE x1.N + x10.N*10 + x100.N*100 + x1000.N*1000 <= @max;
它生成 [0, @max] 范围内的整数。
【讨论】:
所有其他答案都很好,但是它们都存在较大范围的速度问题,因为它们强制 MySQL 生成每个数字然后过滤它们。
以下仅使 MySQL 生成所需的数字,因此速度更快:
set @amount = 55; # How many numbers from zero you want to generate
select `t0`.`i`+`t1`.`i`+`t2`.`i`+`t3`.`i` as `offset`
from
(select 0 `i` 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) `t0`,
(select 0 `i` union select 10 union select 20 union select 30 union select 40 union select 50 union select 60 union select 70 union select 80 union select 90) `t1`,
(select 0 `i` union select 100 union select 200 union select 300 union select 400 union select 500 union select 600 union select 700 union select 800 union select 900) `t2`,
(select 0 `i` union select 1000 union select 2000 union select 3000 union select 4000 union select 5000 union select 6000 union select 7000 union select 8000 union select 9000) `t3`
where `t3`.`i`<@amount
and `t2`.`i`<@amount
and `t1`.`i`<@amount
and `t0`.`i`+`t1`.`i`+`t2`.`i`+`t3`.`i`<@amount;
使用上述方法,您最多可以生成 10,000 个数字(0 到 9,999),而对于较小的数字,无论它们有多低,都不会产生较慢的速度开销。
【讨论】:
如果你有 MySql 8 及以上版本,这里有一种使用 json_table 的方法:
set @noRows = 100;
SELECT tt.rowid - 1 AS value
FROM JSON_TABLE(CONCAT('[{}', REPEAT(',{}', @noRows - 1), ']'),
"$[*]" COLUMNS(rowid FOR ORDINALITY)
) AS tt;
(h/t - https://www.percona.com/blog/2020/07/27/generating-numeric-sequences-in-mysql/)
【讨论】:
使用递归 cte..
with recursive rnums as (
select 1 as n
union all
select n+1 as n from rnums
where n <10
)
select * from rnums
;
结果将是.. +------+ | n | +------+ | 1 | | 2 | | 3 | | 4 | | 5 | | 6 | | 7 | | 8 | | 9 | | 10 | +------+ 10 行一组(0.00 秒)
【讨论】:
在 MariaDB 中你可以这样做:
从 seq_i_to_N 中选择 *
例如:
从 seq_0_to_1000 中选择 *
从 seq_1_to_1000000 中选择 *
参考:https://www.percona.com/blog/2020/07/27/generating-numeric-sequences-in-mysql/
【讨论】: