SQL 生成连续的数值,你希望有一个“行数据生成器”。如果你的查询里需要数据透视表,就用得着这个行数据生成器。例如,你想返回如下所示的结果集,并且可以为它指定任意数目的行数据。
SQL 生成连续的数值 问题描述
你希望有一个“行数据生成器”。如果你的查询里需要数据透视表,就用得着这个行数据生成器。例如,你想返回如下所示的结果集,并且可以为它指定任意数目的行数据。
ID
---
1
2
3
4
5
6
7
8
9
10
...
如果数据库提供了可以动态地生成行数据的内置函数,那么就不需要预先创建一个固定行数的数据透视表。这就是动态的行数据生成器如此有用的原因。否则,我们必须借助一个传统的、行数固定(也许并不够用)的数据透视表来生成所需的行数据。
SQL 生成连续的数值 解决方案
本解决方案展示如何返回从 1 开始递增至 10 的 10 行数据。我们也可以简单地改动一下代码,以返回任意数目的行。
可以返回从 1 开始递增的值,这种能力为其他许多问题的解决方案打开了方便之门。例如,我们可以生成数字,并加上日期值,这样就能生成连续的日期了。也可以借助这些数字来解析字符串。
DB2 和 SQL Server
使用 WITH
递归查询生成一系列含有递增值的行。先借助一个像 T1
这样的只有 1 行数据的表来启动行数据生成操作,其余的交给 WITH
子句即可。
1 with x (id)
2 as (
3 select 1
4 from t1
5 union all
6 select id+1
7 from x
8 where id+1 <= 10
9 )
10 select * from x
下面是另一个替代方案,只适用于 DB2。该方案的优点是不需要 T1
表。
1 with x (id)
2 as (
3 values (1)
4 union all
5 select id+1
6 from x
7 where id+1 <= 10
8 )
9 select * from x
Oracle
使用 CONNECT BY
递归查询(适用于 Oracle 9i 及后续版本)。如果使用的是 Oracle 9i,我们要么把 CONNECT BY
放入一个内嵌视图,要么把它放进 WITH
子句。
1 with x
2 as (
3 select level id
4 from dual
5 connect by level <= 10
6 )
7 select * from x
对于 Oracle Database 10g 及后续版本,则可以用 MODEL
子句生成行数据。
1 select array id
2 from dual
3 model
4 dimension by (0 idx)
5 measures(1 array)
6 rules iterate (10) (
7 array[iteration_number] = iteration_number+1
8 )
PostgreSQL
使用 GENERATE_SERIES
函数,该函数就是为快速生成行数据而设计的。
1 select id
2 from generate_series (1,10) x(id)
SQL 生成连续的数值 扩展知识
DB2 和 SQL Server
WITH
递归查询逐步递增 ID
(初始值为 1),直到背离了 WHERE
子句的条件为止。为了开启递归操作,需要先生成第一行数据,这一行里包含的值应该是 1。我们可以通过 SELECT 1 FROM T1
实现这一点,T1
表只包含一行数据;对于 DB2 而言,还可以使用 VALUES
子句生成只含有一行数据的结果集。
Oracle
本解决方案把 CONNECT BY
子查询放进了 WITH
子句。在 WHERE
子句中断之前,行数据会被连续生成出来。Oracle 会自动递增伪列 LEVEL
的值,我们不必再做什么。
在 MODEL
子句解决方案里,有一个显式的 ITERATE
命令,该命令帮助生成多行数据。如果没有 ITERATE
子句,则只返回一行数据,因为 DUAL
表仅包含一行数据。
select array id
from dual
model
dimension by (0 idx)
measures(1 array)
rules ()
ID
--
1
MODEL
子句不仅能让我们像访问数组一样访问行数据,还允许我们方便地创建新的行或返回表里不存在的行。在本解决方案中,IDX
是数组下标(数组里某个特定值的位置),ARRAY
(别名 ID
)是行数据构成的“数组”。第一行的默认值是 1,可以通过 ARRAY[0]
来访问。Oracle 提供了 ITERATION_NUMBER
函数,以便我们知道迭代次数。本解决方案迭代了 10 次,因而 ITERATION_NUMBER
从 0 增加到了 9。为每个 ITERATION_NUMBER
值加上 1,结果就是从 1 到 10。
执行以下查询,会更便于我们理解 MODEL
子句的作用。
select 'array['||idx||'] = '||array as output
from dual
model
dimension by (0 idx)
measures(1 array)
rules iterate (10) (
array[iteration_number] = iteration_number+1
)
OUTPUT
------------------
array[0] = 1
array[1] = 2
array[2] = 3
array[3] = 4
array[4] = 5
array[5] = 6
array[6] = 7
array[7] = 8
array[8] = 9
array[9] = 10
PostgreSQL
全部工作都交给 GENERATE_SERIES
函数来完成。该函数有 3 个参数,它们都是数值类型。第一个参数是初始值,第二个参数是结束值,第三个参数是可选项,代表“步长”(每次增加的值)。如果没有指定第 3 个参数,则默认每次增加 1。
GENERATE_SERIES
函数功能强大,我们传递给它的参数甚至可以不是常量。例如,我们希望返回 5 行数据,初始值为 10,结束值为 30,步长为 5,那么就应该会得到如下所示的结果集。
ID
---
10
15
20
25
30
为了达到上述目的,我们甚至可以写出类似这样的代码。
select id
from generate_series(
(select min(deptno) from emp),
(select max(deptno) from emp),
5
) x(id)
需要注意的是,我们在编写以上查询代码的时候并不知道要传递给 GENERATE_SERIES
函数的参数值是什么。只有当实际执行主查询的时候,那两个子查询才会把实际的参数值计算出来。