【问题标题】:Generate an integer sequence in MySQL在 MySQL 中生成整数序列
【发布时间】:2010-09-23 04:45:59
【问题描述】:

我需要使用包含整数 nm 的表/结果集/任何内容进行连接。有没有一种简单的方法可以在不建立表格的情况下获得它?

(顺便说一句,这种类型的构造叫什么,“元查询”?)

m-n 被限制在合理的范围内(

【问题讨论】:

  • 当需要同时包含父母和孩子时,您可能会遇到 auto_increment 的麻烦,我从不使用它,nextval 更简单。你可以在这里看到 mysql 代码的 nextval 函数:stackoverflow.com/questions/8058675/…

标签: mysql


【解决方案1】:

我在网上找到了这个解决方案

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

单次查询,速度快,完全符合我的要求:现在我可以“编号”从复杂查询中找到的“选择”,其中唯一编号从 1 开始,并为结果中的每一行递增一次。

我认为这也适用于上面列出的问题:调整@row 的初始起始值并添加限制子句来设置最大值。

顺便说一句:我认为“r”并不是真正需要的。

ddsp

【讨论】:

  • 是的,需要 r - 错误 1248:每个派生表都必须有自己的别名
  • 选择的行数来自some_table而不是r。如果 some_table 没有行,你什么也得不到。我用它来生成一组只需要名称和密码的测试数据。数字序列变成了名字,密码只是 ENCRYPT('passwd') 所以它们都是一样的。为了生成行,我从另一个表中选择,但实际上并没有选择任何列。它只是为some_table 中的每一行提供了一行,所有这些都带有序号。
  • +很多,我有点震惊——不知道 MySQL 可以做到这一点。例如,这两个单独的查询可用于对表的子集重新排序:SET @row := 0UPDATE foo SET position = (@row := @row + 1) WHERE <conditions> ORDER BY last_modified ASC
  • 从 C# 执行这个会给出错误,“必须定义参数'@row'。”但我在这里找到了解决方案:stackoverflow.com/questions/958953/…
  • @Izkata,David,这种行为是否受到规范的保证?如您所知,未来的版本可能会为所有行提供相同的值,并且只在整个选择之后进行增量。
【解决方案2】:

下面会返回 1..10000 并没有那么慢

SELECT @row := @row + 1 AS row FROM 
(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) t,
(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) t2, 
(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) t3, 
(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) t4, 
(SELECT @row:=0) numbers;

【讨论】:

  • 一个解释就好了-t - t4每个包含10行虚拟数据,这些的笛卡尔积是10^4,即10000行虚拟数据,而外部选择只是mysql版本的行号
  • @Aprillion,2^14 会做同样的事情。只有 28 个值而不是 40 个。
  • 您可以通过将“@row := @row + 1 as row”替换为“concat(t.0,t2.0,t3.0,t4.0) 来摆脱变量声明+ 1 作为行”,然后按“行”排序。要获得准确的结果,您还需要将每个子查询中的重复“select 6”更改为“select 2”(“2”当前未使用,但“6”列出了两次。)。作为奖励,您还可以将每个“union all”缩短为“union”,因为不应再有任何重复的行。
  • @Seth McCauley:无需将 6 更改为 2。甚至没有使用这些值。还不如全部为空。只需要@row := @row + 1 as row 来获取每行的递增数字。联合只是乘以给出行数。
  • 这最初对我来说失败了,错误是“每个派生表都必须有自己的别名”。通过给最终的 SELECT 一个别名来轻松修复 - 例如在最后添加t5
【解决方案3】:

如果您碰巧使用MariaDB fork of MySQLthe SEQUENCE engine 允许直接生成数字序列。它通过使用虚拟(假)一列表来做到这一点。

例如,要生成从 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

对于以“2010-01-01”开头的连续十年的DATE 值,请执行此操作。

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,请考虑一下。

【讨论】:

  • 我在这里尝试过:sqlzoo.net/wiki/Guest_House_Assessment_Hard,但没有成功。然而,很有可能是因为在浏览器中我们正在使用一个包装好的 MariaDB,所以这只是因为这个原因而不起作用
  • 这是一个了不起的解决方案,可让您进行许多思考,例如根据每天、每周、每月、每年、每两周等频率运行的重复计划计算预计收入。跨度>
【解决方案4】:

试试这个.. 它适用于我的 mysql 版本 8.0。 您可以根据您需要的范围修改以下查询

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

是的,没有创建您帖子中提到的表格

【讨论】:

  • 不错的解决方案,但是,这适用于小于 1000 的数字,这是 cte_max_recursion_depth 的默认值
【解决方案5】:

MySQL 中没有序列号生成器 (CREATE SEQUENCE)。最接近的是AUTO_INCREMENT,可以帮你建表。

【讨论】:

  • 到现在 MySQL 8 还不支持这个特性。它是在 MariaDB 和 PostgreSQL 中实现的。
【解决方案6】:

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

结果将是 121 和 129 之间的序列号 122 和 124 的间隙:

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

也许它对某人有帮助!

【讨论】:

    【解决方案7】:

    有一种方法可以在单个查询中获取一系列值,但它有点慢。它可以通过使用缓存表来加速。

    假设您想要一个包含所有 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';
    

    请注意,此技巧非常缓慢,仅允许在预定义域中创建有限序列(例如 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(*) 将需要很长时间,如果您在 MAX-MIN 大于 100k 时错误地添加 ORDER BY,则将再次需要几秒钟来计算...

    【讨论】:

      【解决方案8】:

      你可以试试这样的:

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

      order 只是一些具有相当大的行集的表的示例。

      编辑:最初的答案是错误的,任何功劳都应该归功于 David Poor,他提供了相同概念的工作示例

      【讨论】:

      • 我将不得不解开那个谜题,但它看起来确实很整洁!
      • 基本上最内部的选择将会话变量初始化为 2,然后外部选择为每一行增加这个变量,在这种情况下,笛卡尔连接到 order 意味着变量将获得与序列的关系从 2 到订单数 + 2 的数字(限制 4 将此限制为 4 个数字)
      • 这个“解决方案”似乎需要一个现有的表order,其中包含可能匹配整数的完整列表。如果我尝试运行而不加入已经存在有效整数的大型预先存在的表,则除了单行结果外,我无法获得任何结果。
      • @rektide,它没有“出现”。它错误的,根本不起作用。
      • 哦,这是很久以前的事了。我认为我的意图是像采购订单中的“订单”(当时我正在做 ERP 系统,在那种系统中,可以预期这样的表会相当大)。但不,我不认为我的意图是使用该表中的实际整数。我看看能不能改进一下。
      【解决方案9】:

      从 1 到 1000 的计数器:

      • 无需建表
      • 执行时间 ~ 0.0014 秒
      • 可以转换成视图
          select tt.row from
          (
          SELECT cast( concat(t.0,t2.0,t3.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
      

      致谢:answer,Seth McCauley 在答案下方发表评论。

      【讨论】:

        【解决方案10】:

        你似乎能够用以下方法构造相当大的集合:

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

        我在 5.0.51a 的 5300 中遇到了解析器堆栈溢出。

        【讨论】:

        • 哇,哇!你真的测试过?!哇!
        • @BCS,在您的网址栏中输入:data:text/html,&lt;script&gt;for(var x=1;x&lt;=5678;++x)document.write((x===1?'':' union all ')+'select '+x);&lt;/script&gt;
        • @Pacerier 很棒的技术,不仅仅适用于这个应用程序。
        【解决方案11】:

        此查询生成从 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
        

        【讨论】:

          【解决方案12】:

          m 有多大?

          你可以这样做:

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

          (非临时的二表可以通过在创建临时表seq中用(select null foo union all select null)替换二来省略。)

          【讨论】:

          • 我有点喜欢这个概念,但如果我无论如何都必须构建表,我只会做一个自动增量并手动添加行,直到它足够大。
          【解决方案13】:

          这是此处其他答案中使用的技术的紧凑二进制版本:

          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,但您可以轻松调整。

          【讨论】:

            【解决方案14】:

            最简单的方法是:

            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。有时需要。

            我的回答受到David Poor answer 的启发。谢谢大卫!

            【讨论】:

              【解决方案15】:

              警告:如果您一次插入一行数字,您最终会执行 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
              

              编辑:仅供参考,不幸的是,这不适用于真正的temporary table with MySQL 5.0,因为它无法插入自身(您可以在两个临时表之间来回切换)。

              编辑:您可以使用MEMORY storage engine 来防止这实际上是对“真实”数据库的消耗。我想知道是否有人开发了一个“NUMBERS”虚拟存储引擎来实例化虚拟存储来创建这样的序列。 (唉,在 MySQL 之外不可移植)

              【讨论】:

              • 一个不错的 KISS 解决方案。 OTOH它确实实现了整个事情;(。+1
              • 是的。太糟糕了,没有 SELECT number FROM INTEGERS WHERE number > 0 AND number
              • hmmmm...有人-1'd这个答案,有什么特别的原因吗?
              • ...有人花了 5 年时间才弄明白为什么?
              【解决方案16】:

              如果您使用的是 Oracle,那么“流水线函数”将是您的最佳选择。不幸的是,MySQL 没有这样的结构。

              根据您想要的数字的规模,我看到了两种简单的方法:您可以使用单个查询所需的数字(可能使用存储过程填充的内存表)填充临时表或者,在前面,您构建一个从 1 到 1,000,000 的大表并选择它的有界区域。

              【讨论】:

                【解决方案17】:
                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;
                

                无论您想要多大的表(10 的 n 次方),都可以继续为 t1 表起别名。然后可以添加一个限制X来切断它。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2012-06-21
                  • 2013-02-13
                  • 1970-01-01
                  • 1970-01-01
                  • 2010-09-16
                  • 1970-01-01
                  • 1970-01-01
                  • 2019-10-02
                  相关资源
                  最近更新 更多