您的位置:首页 > 数据库

Microsoft SQL Server 如何生成连续的数字/字符/时间序列

2020-07-19 04:07 1701 查看

文章目录

  • 使用通用表表达式生成序列
  • 使用表值函数生成序列
  • 大家好,我是只谈技术不剪发的 Tony 老师。有时候我们需要在数据库中生成一些连续的数字、字符或者时间序列值,今天我们继续介绍如何在 Microsoft SQL Server 中实现这个功能。

    📝如果你使用的是 MySQL 数据库,实现相同的功能可以参考这篇文章
    📝如果你使用的是 Oracle 数据库,实现相同的功能可以参考这篇文章
    📝如果你使用的是 PostgreSQL ,实现相同的功能可以参考这篇文章

    使用系统视图生成序列

    Microsoft SQL Server 中的系统视图,例如 master.dbo.spt_values,可以用于生成数字序列。

    生成一个连接的数字序列

    以下语句使用视图 master.dbo.spt_values 生成了一个连续的数字序列:

    select number
    from master.dbo.spt_values
    where type = 'P'
    and number between 11 and 15;
    number|
    ------|
    11|
    12|
    13|
    14|
    15|

    这种方法可以生成从 0 到 2047 之间的连续数字序列。

    为了生成更大范围的数字序列,我们可以基于该视图创建一个新的视图,例如:

    create view generator
    as
    select t1.number * 2048 + t2.number as number
    from master.dbo.spt_values t1
    cross join master.dbo.spt_values t2
    where t1.type = 'P' and t2.type = 'P';

    视图 generator 可以生成 0 到 4194303 之间的数字序列:

    select min(number) as min, max(number) as max, count(number) as count
    from generator;
    min|max    |count  |
    ---|-------|-------|
    0|4194303|4194304|

    生成一个间隔的数字序列

    求余运算符(%)可以用于返回指定增量的间隔数字序列,例如:

    select number
    from master.dbo.spt_values
    where type = 'P'
    and number between 1 and 10
    and number % 3 = 1;
    number|
    ------|
    1|
    4|
    7|
    10|

    该查询返回了一个从 1 到 10 之间、增量为 3 的数字序列。

    以下查询返回了一个增量为 -2.5、范围从 15 到 1.4 之间的降序数字序列:

    select 15 + number * -2.5 as number
    from master.dbo.spt_values
    where type = 'P'
    and 15 + number * -2.5 >= 1.4;
    number|
    ------|
    15.0|
    12.5|
    10.0|
    7.5|
    5.0|
    2.5|

    生成一个连续的字符序列

    基于上面的视图和 char(n) 函数可以生成连续的字符序列。例如:

    select char(number) as letter
    from master.dbo.spt_values
    where type = 'P'
    and number between 65 and 70;
    letter|
    ------|
    A     |
    B     |
    C     |
    D     |
    E     |
    F     |

    该查询返回了字符 A 到 F 的序列,char(n) 函数用于将 ASCII 编码转化为相应的字符。

    生成一个间隔的时间序列

    同样基于上面的视图和时间加减法可以生成间隔的时间序列。例如:

    select dateadd(hour, number, '2020-01-01 00:00:00') as ts
    from master.dbo.spt_values
    where type = 'P'
    and number between 0 and 12;
    ts                 |
    -------------------|
    2020-01-01 00:00:00|
    2020-01-01 01:00:00|
    2020-01-01 02:00:00|
    2020-01-01 03:00:00|
    2020-01-01 04:00:00|
    2020-01-01 05:00:00|
    2020-01-01 06:00:00|
    2020-01-01 07:00:00|
    2020-01-01 08:00:00|
    2020-01-01 09:00:00|
    2020-01-01 10:00:00|
    2020-01-01 11:00:00|
    2020-01-01 12:00:00|

    该查询返回了 2020-01-01 00:00:00 到 2020-01-01 12:00:00、间隔为 1 小时的所有时间点。

    使用通用表表达式生成序列

    生成一个等差数字序列

    通用表表达式(Common Table Expression)的递归调用可以用于生成各种数列。例如:

    with t(n) as (
    select 0
    union all
    select n+2 from t where n < 10
    )
    select n from t;
    n |
    --|
    0|
    2|
    4|
    6|
    8|
    10|

    以上语句生成了一个 0 到 10之间、增量为 2 的偶数数列,执行过程如下:

    • 首先,执行 CTE 中的初始化查询,生成一行数据(0);
    • 然后,第一次执行递归查询,判断 n < 10,生成一行数据 2(n+2);
    • 接着,重复执行递归查询,生成更多的数据;直到 n = 10 时不满足条件终止递归;此时临时表 t 中包含 6 条数据;
    • 最后,执行主查询,返回所有的数据。

    生成一个等比数字序列

    上文中的系统视图只能用于生成等差数列,通用表表达式则可以生成更复杂的数列,例如等比数列:

    with t(n) as (
    select 1
    union all
    select n*4 from t where n*4 < 1000
    )
    select n from t;
    n  |
    ---|
    1|
    4|
    16|
    64|
    256|

    从第二行开始,每个数字都是上一行的 4 倍。

    生成斐波那契数列

    斐波那契数列(Fibonacci series)是指从数字 0 和 1(或者从 1 和 1)开始,后面的每个数字等于它前面两个数字之和(0、1、1、2、3、5、8、13、21、…)。使用通用表表达式可以很容易地生成斐波那契数列:

    with fibonacci (n, fib_n, next_fib_n) as (
    select 1, 0, 1
    union all
    select n + 1, next_fib_n, fib_n + next_fib_n
    from fibonacci
    where n < 10 )
    select * from fibonacci;
    n |fib_n|next_fib_n|
    --|-----|----------|
    1|    0|         1|
    2|    1|         1|
    3|    1|         2|
    4|    2|         3|
    5|    3|         5|
    6|    5|         8|
    7|    8|        13|
    8|   13|        21|
    9|   21|        34|
    10|   34|        55|

    其中,字段 n 表示该行包含了第 n 个斐波那契数列值;字段 fib_n 表示斐波那契数列值;字段 next_fib_n 表示下一个斐波那契数列值。

    生成一个连续的字符序列

    基于通用表表达式和 char(n) 函数同样可以生成连续的字符序列,例如:

    with t(n) as (
    select 65
    union all
    select n+1 from t where n < 70
    )
    select char(n) as letter from t;
    letter|
    ------|
    A     |
    B     |
    C     |
    D     |
    E     |
    F     |

    生成一个间隔的时间序列

    以下语句使用递归通用表表达式生成一个时间序列:

    with ts(v) as (
    select cast('2020-01-01 00:00:00' as datetime2)
    union all
    select dateadd(hour,1, v) from ts where v < cast('2020-01-01 12:00:00' as datetime2)
    )
    select v from ts;
    v                  |
    -------------------|
    2020-01-01 00:00:00|
    2020-01-01 01:00:00|
    2020-01-01 02:00:00|
    2020-01-01 03:00:00|
    2020-01-01 04:00:00|
    2020-01-01 05:00:00|
    2020-01-01 06:00:00|
    2020-01-01 07:00:00|
    2020-01-01 08:00:00|
    2020-01-01 09:00:00|
    2020-01-01 10:00:00|
    2020-01-01 11:00:00|
    2020-01-01 12:00:00|

    以上查询返回了 2020-01-01 00:00:00 到 2020-01-01 12:00:00 之间,间隔为 1 小时的所有时间点。

    使用表值函数生成序列

    Microsoft SQL Server 支持表值函数(table-valued function),也就是返回结果为集合(表)的函数,可以用于模拟 PostgreSQL 中的 generate_series 函数。

    创建模拟的 generate_series 表值函数

    我们创建一个表值函数 generate_series:

    create or alter function generate_series(@pstart numeric(38,10), @pstop numeric(38,10), @pstep numeric(38,10) = 1.0)
    returns @generate_series table(n numeric(38,10))
    as
    begin
    if @pstep = 0 return
    if @pstart > @pstop and @pstep > 0 return
    if @pstart < @pstop and @pstep < 0 return
    
    ;with t(n, v) as (
    select 1, @pstart
    union all
    select n+1, cast(@pstart + n * @pstep as numeric(38,10)) from t where n <= floor(abs((@pstop-@pstart)/@pstep))
    )
    insert into @generate_series
    select v from t
    
    return
    end;

    其中,@pstart 表示数据序列的起点,@pstop 表示数据序列的终点,@pstep 表示每次的增量,不允许为 0,默认为 1。

    使用 generate_series 函数生成序列

    创建了 generate_series 函数之后,我们就可以用它来生成各种序列值。例如:

    select cast (n as integer) as n
    from generate_series(11, 15, default);
    n |
    --|
    11|
    12|
    13|
    14|
    15|
    
    select n from generate_series(15, 1.4, -2.5);
    n            |
    -------------|
    15.0000000000|
    12.5000000000|
    10.0000000000|
    7.5000000000|
    5.0000000000|
    2.5000000000|

    第一个函数返回了 11 到 15 的连续整数;第二个函数返回了 15 到 1.4 之间增量为 -2.5 的数字序列。

    我们同样可以使用 generate_series 函数生成字符序列和时间序列:

    select char(n) as letter
    from generate_series(65, 70, 1);
    letter|
    ------|
    A     |
    B     |
    C     |
    D     |
    E     |
    F     |
    
    select dateadd(hour, n, timefromparts(0,0,0,0,0)) as time
    from generate_series(0, 12, default);
    time    |
    --------|
    00:00:00|
    01:00:00|
    02:00:00|
    03:00:00|
    04:00:00|
    05:00:00|
    06:00:00|
    07:00:00|
    08:00:00|
    09:00:00|
    10:00:00|
    11:00:00|
    12:00:00|

    如果觉得文章对你有用,欢迎关注❤️、评论📝、点赞赞👍

    内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
    标签: