SQL 生成连续的数值,你希望有一个“行数据生成器”。如果你的查询里需要数据透视表,就用得着这个行数据生成器。例如,你想返回如下所示的结果集,并且可以为它指定任意数目的行数据。
SQL 生成连续的数值 问题描述
你希望有一个“行数据生成器”。如果你的查询里需要数据透视表,就用得着这个行数据生成器。例如,你想返回如下所示的结果集,并且可以为它指定任意数目的行数据。
如果数据库提供了可以动态地生成行数据的内置函数,那么就不需要预先创建一个固定行数的数据透视表。这就是动态的行数据生成器如此有用的原因。否则,我们必须借助一个传统的、行数固定(也许并不够用)的数据透视表来生成所需的行数据。
SQL 生成连续的数值 解决方案
本解决方案展示如何返回从 1 开始递增至 10 的 10 行数据。我们也可以简单地改动一下代码,以返回任意数目的行。
可以返回从 1 开始递增的值,这种能力为其他许多问题的解决方案打开了方便之门。例如,我们可以生成数字,并加上日期值,这样就能生成连续的日期了。也可以借助这些数字来解析字符串。
DB2 和 SQL Server
使用 WITH
递归查询生成一系列含有递增值的行。先借助一个像 T1
这样的只有 1 行数据的表来启动行数据生成操作,其余的交给 WITH
子句即可。
下面是另一个替代方案,只适用于 DB2。该方案的优点是不需要 T1
表。
Oracle
使用 CONNECT BY
递归查询(适用于 Oracle 9i 及后续版本)。如果使用的是 Oracle 9i,我们要么把 CONNECT BY
放入一个内嵌视图,要么把它放进 WITH
子句。
对于 Oracle Database 10g 及后续版本,则可以用 MODEL
子句生成行数据。
PostgreSQL
使用 GENERATE_SERIES
函数,该函数就是为快速生成行数据而设计的。
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
表仅包含一行数据。
MODEL
子句不仅能让我们像访问数组一样访问行数据,还允许我们方便地创建新的行或返回表里不存在的行。在本解决方案中,IDX
是数组下标(数组里某个特定值的位置),ARRAY
(别名 ID
)是行数据构成的“数组”。第一行的默认值是 1,可以通过 ARRAY[0]
来访问。Oracle 提供了 ITERATION_NUMBER
函数,以便我们知道迭代次数。本解决方案迭代了 10 次,因而 ITERATION_NUMBER
从 0 增加到了 9。为每个 ITERATION_NUMBER
值加上 1,结果就是从 1 到 10。
执行以下查询,会更便于我们理解 MODEL
子句的作用。
PostgreSQL
全部工作都交给 GENERATE_SERIES
函数来完成。该函数有 3 个参数,它们都是数值类型。第一个参数是初始值,第二个参数是结束值,第三个参数是可选项,代表“步长”(每次增加的值)。如果没有指定第 3 个参数,则默认每次增加 1。
GENERATE_SERIES
函数功能强大,我们传递给它的参数甚至可以不是常量。例如,我们希望返回 5 行数据,初始值为 10,结束值为 30,步长为 5,那么就应该会得到如下所示的结果集。
为了达到上述目的,我们甚至可以写出类似这样的代码。
需要注意的是,我们在编写以上查询代码的时候并不知道要传递给 GENERATE_SERIES
函数的参数值是什么。只有当实际执行主查询的时候,那两个子查询才会把实际的参数值计算出来。