【问题标题】:Generating a range of numbers in MySQL在 MySQL 中生成一系列数字
【发布时间】:2010-09-16 06:01:03
【问题描述】:

如何从 MySQL 查询中生成一系列连续数字(每行一个),以便我可以将它们插入到表中?

例如:

nr
1
2
3
4
5

我只想为此使用 MySQL(而不是 PHP 或其他语言)。

【问题讨论】:

  • 您想将此添加到现有记录中还是添加到全新表中?
  • 为什么不能使用 auto_increment 列?

标签: sql mysql


【解决方案1】:
DECLARE i INT DEFAULT 0;

WHILE i < 6 DO
  /* insert into table... */
  SET i = i + 1;
END WHILE;

【讨论】:

    【解决方案2】:

    如果您需要表中的记录并且想要避免并发问题,请按照以下步骤操作。

    首先你创建一个表来存储你的记录

    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
    

    【讨论】:

      【解决方案3】:

      这是一种无需循环的基于集合的方法。这也可以制成视图以供重用。该示例显示了从 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
      

      【讨论】:

        【解决方案4】:

        这是硬件工程师版本的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;
        

        【讨论】:

        • 目的是生成一个只有在运行时才知道大小的序列,而不使用动态 SQL。
        【解决方案5】:

        假设您想在表格中插入数字 1 到 100。只要您有其他一些至少有那么多行的表(与表的内容无关),那么这是我的首选方法:

        INSERT INTO pivot100 
        SELECT @ROW := @ROW + 1 AS ROW
         FROM someOtherTable t
         join (SELECT @ROW := 0) t2
         LIMIT 100
        ;
        

        想要一个以非 1 开头的范围?只需更改 @ROW 在联接中设置的内容即可。

        【讨论】:

        • 这个看起来像个 hack,但我需要大约 7,000 个数字,而且我已经有一个有 70,000 行的表,所以它工作得非常好!
        • 这似乎是最好的解决方案。一直使用这个答案非常频繁,它执行得非常快。它只是工作!
        【解决方案6】:

        大家都明白,这是相当hacky,所以要小心使用

        SELECT
          id % 12 + 1 as one_to_twelve
        FROM
          any_large_table
        GROUP BY
          one_to_twelve
        ;
        

        【讨论】:

        • 这正是我所需要的:我有一个带有自动递增 id 的大表,它定义了一个结构,我想知道有多少行被删除,有多少 id 可以在不让 mysql auto 的情况下被重用- 生成密钥。非常感谢!
        【解决方案7】:

        我知道(在 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;
        

        【讨论】:

          【解决方案8】:

          我想分享的想法不是对问题的准确回答,但可能对某些人有用,所以我想分享它。

          如果您经常只需要一组有限的数字,那么创建一个包含您可能需要的数字的表格并每次都使用该表格会很有帮助。例如:

          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
          

          【讨论】:

            【解决方案9】:

            与接受的响应非常相似,但使用 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;
            

            【讨论】:

              【解决方案10】:

              这是基于之前的答案 (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] 范围内的整数。

              【讨论】:

                【解决方案11】:

                所有其他答案都很好,但是它们都存在较大范围的速度问题,因为它们强制 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),而对于较小的数字,无论它们有多低,都不会产生较慢的速度开销。

                【讨论】:

                  【解决方案12】:

                  如果你有 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/)

                  【讨论】:

                    【解决方案13】:

                    使用递归 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 秒)

                    【讨论】:

                      【解决方案14】:

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

                      【讨论】:

                        猜你喜欢
                        • 2021-10-01
                        • 1970-01-01
                        • 1970-01-01
                        • 2013-07-29
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 2022-01-26
                        相关资源
                        最近更新 更多