【问题标题】:How do I make a row generator in MySQL?如何在 MySQL 中创建行生成器?
【发布时间】:2010-10-16 15:09:32
【问题描述】:

有没有办法生成任意数量的行,可以在类似于 Oracle 语法的 JOIN 中使用:

SELECT LEVEL FROM DUAL CONNECT BY LEVEL<=10

【问题讨论】:

  • 能否请您发布您要编写的查询?

标签: sql mysql generator


【解决方案1】:

我不知道这是否有帮助,但您可以使用 sth 对每个 select 语句中的行进行编号。喜欢:

SET @NUM = 0;

SELECT @NUM:=@NUM+1 行号,* FROM ...

稍后加入他们的行列。 在大型数据库中,这可能非常慢。

【讨论】:

    【解决方案2】:

    不想这么说,但MySQL 是四大中唯一没有此功能的RDBMS

    Oracle:

    SELECT  *
    FROM    dual
    CONNECT BY
            level < n
    

    MS SQL 中(最多100 行):

    WITH hier(row) AS
            (
            SELECT  1
            UNION ALL
            SELECT  row + 1
            FROM    hier
            WHERE   row < n
            )
    SELECT  *
    FROM    hier
    

    或使用提示直到32768

    WITH hier(row) AS
            (
            SELECT  1
            UNION ALL
            SELECT  row + 1
            FROM    hier
            WHERE   row < 32768
            )
    SELECT  *
    FROM    hier
    OPTION (MAXRECURSION 32767) -- 32767 is the maximum value of the hint
    

    PostgreSQL:

    SELECT  *
    FROM    generate_series (1, n)
    

    MySQL,什么都没有。

    【讨论】:

    【解决方案3】:

    如果我理解你的话,你想要一个连续数字的列表吗?

    列出清单:

    create table artificial_range (id int not null primary key auto_increment, idn int);
    insert into artificial_range (idn) values (0); --first row
    insert into artificial_range(idn) select idn from artificial_range; --2nd
    insert into artificial_range(idn) select idn from artificial_range; -- now 4 rows
    insert into artificial_range(idn) select idn from artificial_range; --8
    insert into artificial_range(idn) select idn from artificial_range; --16
    insert into artificial_range(idn) select idn from artificial_range; --32
    insert into artificial_range(idn) select idn from artificial_range; --64
    insert into artificial_range(idn) select idn from artificial_range; --128
    

    ...等等,直到你有,比如说,1024。

    update artificial_range set idn = id - 1 ; 
    

    -- 现在你有一个从 1 (id) 开始的系列和一个从 0 开始的系列

    现在加入它,或者加入它的转换:

        create view days_this_century as 
    select date_add('2000-01-01', interval a.idn day) as cdate 
    from artificial_range;
    

    【讨论】:

    • 这正是我希望避免的。
    【解决方案4】:

    在 MySql 中,据我了解,您可以使用没有表(或 DUAL)的 SELECT 获得多行。

    因此,要获得多行,您确实需要一个至少包含所需行数的真实或临时表。

    但是,您不需要构建临时表,因为您可以使用 ANY 至少具有所需行数的现有表。因此,如果您的表至少具有所需的行数,请使用:

    SELECT  @curRow := @curRow + 1 AS row_number
    FROM    sometable 
    JOIN    (SELECT @curRow := 0) r
    WHERE   @curRow<100;
    

    只需将“sometable”替换为您的任何表的名称,其中至少有所需的行数。

    PS:“r”是一个表“别名”:我可以使用“AS r”。 FROM 或 JOIN 子句中的任何子查询都会创建一个“派生表”,与所有表一样,该表必须具有名称或别名。 (见MySql手册:13.2.9.8. FROM子句中的子查询)

    【讨论】:

      【解决方案5】:

      我有一个包含数字 x 的列 (c5) 的表,我需要一个重复相同行 x 次的 SQL 表达式。

      我的表 A 包含:

      c1  c2  c3  c4  c5
      16  1   2   16  3
      16  1   2   17  2 
      16  1   2   18  1
      

      而我需要:

      c1  c2  c3  c4  c5  n
      16  1   2   16  3   1
      16  1   2   16  3   2
      16  1   2   16  3   3
      16  1   2   17  2   1
      16  1   2   17  2   2
      16  1   2   18  1   1
      

      我用表达式解决了这个问题:

      SELECT
          c1, c2, c3, c4, c5, row_number AS n
      FROM
          (
              SELECT
                  @curRow := @curRow + 1 AS row_number
              FROM
                  tablea
              JOIN (SELECT @curRow := 0) r
              WHERE
                  @curRow < (
                      SELECT
                          max(field1)
                      FROM
                          tablea
                  )
          ) AS vwtable2
      LEFT JOIN tablea d ON vwtable2.row_number <= tablea.field1;
      

      【讨论】:

        【解决方案6】:

        由于这是目前 Google 中“mysql 行生成器”的首批结果之一,因此我将添加更新。

        如果您使用的 MySQL 恰好是 MariaDB,那么他们就有这个功能。它被称为"Sequence Storage engine",它是这样使用的:

        select * from seq_1_to_10;
        

        有了结果:

        +-----+
        | seq |
        +-----+
        |   1 |
        |   2 |
        |   3 |
        |   4 |
        |   5 |
        |   6 |
        |   7 |
        |   8 |
        |   9 |
        |  10 |
        +-----+
        10 rows in set (0.00 sec)
        

        在 10.0 版之前,它是一个需要显式安装的单独插件,但从 10.0 版开始,它是内置的。享受吧!

        【讨论】:

        • 创建十条不同日期的记录示例:INSERT INTO T1 (id, created_at) SELECT one.X, two.X FROM (select seq, seq X from seq_1_to_10) one LEFT JOIN (select seq, NOW() - INTERVAL seq DAY X from seq_1_to_10) two ON one.seq = two.seq
        【解决方案7】:

        要生成 10 行:

        SELECT a AS 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 from dual

        您可以生成 100 行与另外 10 行进行连接:

        select t2.a*10 + t1.a from (SELECT 0 AS 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 AS 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) t2

        然后 1000 行与另一个连接,...

        【讨论】:

          【解决方案8】:

          可以,但是有点麻烦,

          这里是:

          mysql> create table t(inc bigint primary key auto_increment);
          Query OK, 0 rows affected (0.03 sec)
          
          mysql> insert into t values(0);
          Query OK, 1 row affected (0.01 sec)
          
          mysql> insert into t select 0 from t;
          Query OK, 1 row affected (0.02 sec)
          Records: 1  Duplicates: 0  Warnings: 0
          
          mysql> insert into t select 0 from t;
          Query OK, 2 rows affected (0.02 sec)
          Records: 2  Duplicates: 0  Warnings: 0
          
          mysql> insert into t select 0 from t;
          Query OK, 4 rows affected (0.00 sec)
          Records: 4  Duplicates: 0  Warnings: 0
          
          mysql> insert into t select 0 from t;
          Query OK, 8 rows affected (0.01 sec)
          Records: 8  Duplicates: 0  Warnings: 0
          
          mysql> select count(inc), max(inc) from t;
          +------------+----------+
          | count(inc) | max(inc) |
          +------------+----------+
          |         16 |       20 |
          +------------+----------+
          1 row in set (0.00 sec)
          
          mysql> select row_number() over w as inc from t window w as (order by inc);
          +-----+
          | inc |
          +-----+
          |   1 |
          |   2 |
          |   3 |
          |   4 |
          |   5 |
          |   6 |
          |   7 |
          |   8 |
          |   9 |
          |  10 |
          |  11 |
          |  12 |
          |  13 |
          |  14 |
          |  15 |
          |  16 |
          +-----+
          16 rows in set (0.00 sec)
          

          您可以通过发出语句将生成的行数加倍

          insert into t select 0 from t;
          

          只要你需要。

          另请注意,默认情况下 auto_increment 会产生一些间隙,这就是使用 row_number() 的原因。

          【讨论】:

            【解决方案9】:

            MySQL 8.0

            对于 MySQL 8.0、MariaDB 10.2 及更高版本,您可以使用递归 CTE:

            WITH RECURSIVE sequence AS (
                SELECT 1 AS level
                UNION ALL
                SELECT level + 1 AS value
                FROM sequence
                WHERE sequence.level < 10
            )
            SELECT level
            FROM sequence;
            

            请注意,CTE 在 MySQL 中受 cte_max_recursion_depth(默认 1000,最大 4,294,967,295 (2³²−1))和在 MariaDB 中受 max_recursive_iterations(默认 4,294,967,295)限制。

            你可以通过执行来增加限制:

            SET cte_max_recursion_depth = 4294967295;
            

            它只会影响你当前的会话,不会被持久化。

            MySQL 5.7、5.6 及更低版本

            对于 MySQL 8.0 之前的版本,您可以使用下面Markus Winand 的巧妙技巧:

            CREATE OR REPLACE VIEW generator_16
            AS SELECT 0 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   UNION ALL SELECT 10 UNION ALL SELECT 11 UNION ALL
               SELECT 12  UNION ALL SELECT 13 UNION ALL SELECT 14 UNION ALL
               SELECT 15;
            
            CREATE OR REPLACE VIEW generator_256
            AS SELECT ( ( hi.n << 4 ) | lo.n ) AS n
               FROM generator_16 lo, generator_16 hi;
            
            CREATE OR REPLACE VIEW generator_4k
            AS SELECT ( ( hi.n << 8 ) | lo.n ) AS n
               FROM generator_256 lo, generator_16 hi;
            
            CREATE OR REPLACE VIEW generator_64k
            AS SELECT ( ( hi.n << 8 ) | lo.n ) AS n
               FROM generator_256 lo, generator_256 hi;
            
            CREATE OR REPLACE VIEW generator_1m
            AS SELECT ( ( hi.n << 16 ) | lo.n ) AS n
               FROM generator_64k lo, generator_16 hi;
            
            CREATE OR REPLACE VIEW generator_16m
            AS SELECT ( ( hi.n << 16 ) | lo.n ) AS n
               FROM generator_64k lo, generator_256 hi;
            
            CREATE OR REPLACE VIEW generator_4b
            AS SELECT ( ( hi.n << 16 ) | lo.n ) AS n
               FROM generator_64k lo, generator_64k hi;
            

            然后:

            SELECT n FROM generator_4b limit 10;
            

            在我的笔记本电脑上创建包含超过 40 亿行的 generator_4b 只需要大约 20 毫秒。上面所有的生成器视图加起来只占用 28 KB 的存储空间。

            如果你想知道它是如何工作的,你可以在他的blog post中找到详细的解释。

            【讨论】:

              猜你喜欢
              • 2018-12-18
              • 2017-10-30
              • 2015-10-29
              • 1970-01-01
              • 1970-01-01
              • 2017-05-12
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多