SQL Server中的RAND函数的介绍和区间随机数值函数的实现

工作中会遇到sql server模拟数据生成以及数值列值(如整型、日期和时间数据类型)随机填充等等任务,这些任务中都要使用到随机数。鉴于此,本文将对sql server中随机数的使用简单做个总结 。

t-sql 随机有关的三个函数

   rand([seed] 此函数生成从0到1之间随机 float 值(详细说明查看)。

    checksum ( * | expression [ ,…n ] ) 此函数生成按照表的某一行或一组表达式计算出来的int校验和值,checksum 用于生成哈希索引(详细说明查看)。

    newid ( ) 此函数生成 uniqueidentifier 类型的唯一值(详细说明查看)。

生成任意一个随机数值(如整数、日期和时间数据类型)

    如果获得任意一个随机整数值?函数rand生成的结果是float数据类型的,显然很难满足要求,不过可以通过对函数rand的结果继续加工(比如其结果乘以某一个值保留整数部分等等)得到要求。很显然,函数checksum生成的结果是int数据类型,很容易满足我们的结果,不过其参数如果固定(表的某一行值相同或一组表达式值相同),那么其结果也是相同的。函数newid可以保证结果的唯一,但是其结果是unigueidentifer数据类型的。

    从以上三个函数的结果值分析:函数rnad和checksum的结果是能获得整数数值的。如果我们将函数newid的结果值作为函数checksum的参数,那么其每次生成的结果值都是不一样的int数据类型的数值。以下t-sql代码如下:

select checksum(newid()) as checksumvalue, checksum(newid()) as checksumvalue2;
go

执行后的查询结果如下:

 

从上面的查询结果看到组合生成的整数数值都是9位数的,平时工作中大多使用的随机整数值都是不太大的且都是自然数(0和正整数的集合),这就要求限制随机生成的整数数值。可以使用函数abs对其结果进行处理得到任意一个自然数。这样,组合一起来的格式就是这样的:abs(checksum(newid()))。为了便于使用便于使用我们通过将其封装到函数中,但是函数newid又不能在函数中使用,那我们就要考虑其他的方式:将函数newid封装在单列单行的视图中。其定义视图的t-sql代码如下:

 if object_id(n'dbo.vrandomguid', 'v') is not null
 begin
   drop view dbo.vrandomguid;
 end
 go
 --==================================
 -- 功能: 随机guid视图
 -- 说明: 具体实现阐述 
 -- 作者: xxx
 -- 创建: yyyy-mm-dd
 -- 修改: yyyy-mm-dd xxx 修改内容描述
 --==================================
 create view dbo.vrandomguid
   --$encode$--
 as
   select randomguid = newid();
 go
 调用该视图的t-sql代码如下:
 select top randomguid
 from dbo.vrandomguid;
 go 

执行后的查询结果如下:

生成整数区间内的任意一个随机整数

上面的视图定义,我们继续讲解组合函数的进一步封装。我们先讲解如何限制随机生成的数值,取模运算可以实现生成在指定数值区间内的任意一个数值,例如:获取区间[3,5]内任意一个数字,我们设为区间的最小值为@intmin:3,最大值为@intmax,则该区间的间隔值为 @intmax – @intmin + 1: 5 – 3 + 1(3),那么针对任意的整数值设为为@intvalue,然后对这个间隔值先进行取模运算在加上区间最小值,最后得到的值为: @intvalue % (@intmax – @intmin + 1) + @intmin。如果@intvalue为8,则结果值为5;如果@intvalue为9,则结果值为3;如果@intvalue为10,则结果值为4……

    针对以上的分析我们封装的t-sql代码如下:

 if object_id(n'dbo.ufn_randnum', 'fn') is not null
 begin
   drop function dbo.ufn_randnum;
 end
 go
 --==================================
 -- 功能: 获取区间内的任意一个随机数值
 -- 说明: 具体实现阐述 
 -- 作者: xxx
 -- 创建: yyyy-mm-dd
 -- 修改: yyyy-mm-dd xxx 修改内容描述
 -- 调用: select dbo.ufn_randnum(, );
 --==================================
 create function dbo.ufn_randnum
 (
   @intmin int,      -- 随机数值的最小值
   @intmax int        -- 随机数值的最大值
 ) returns int
   --$encode$--
 as
 begin  
   set @intmin = isnull(@intmin, );
   set @intmax = isnull(@intmax, );
   declare @guidvalue as uniqueidentifier;
   select top @guidvalue = randomguid
   from dbo.vrandomguid;
   return abs(checksum(@guidvalue)) % (@intmax - @intmin + ) + @intmin;
 end
 go 

调用以上函数的t-sql代码如下:

 select dbo.ufn_randnum(, ) as randnum, dbo.ufn_randnum(, ) as randnum;
 go 

执行后的查询结果如下:

 

生成日期区间内的任意一个随机日期

    那么我们继续讲解 获取指定区间内的任意一个随机日期,不过我们要使用日期和时间的两个函数:dateadd和datediff。使用datediff计算出日期区间的间隔值,同上的思路我们计算得到的一个随机整数值,然后通过dateadd得到我们期望的一个随机日期。封装函数的t-sql代码如下:

 if object_id(n'dbo.ufn_randdate', 'fn') is not null
 begin
   drop function dbo.ufn_randdate;
 end
 go
 --==================================
 -- 功能: 获取日期区间内的任意一个随机日期
 -- 说明: 具体实现阐述 
 -- 作者: xxx
 -- 创建: yyyy-mm-dd
 -- 修改: yyyy-mm-dd xxx 修改内容描述
 -- 调用: select @dtmrand = dbo.ufn_randdate('--', '--');
 --==================================
 create function dbo.ufn_randdate 
 (
   @dtmmin datetime,  -- 随机日期的最小值
   @dtmmax datetime  -- 随机日期的最大值
 ) returns datetime
   --$encode$--
 as
 begin
   set @dtmmin = isnull(@dtmmin, '--');
   set @dtmmax = isnull(@dtmmax, '--');
   declare @guidvue as uniqueidentifier;
   select top @guidvue = randomguid 
   from dbo.vrandomguid;
   -- 可以将hour换为days
   return dateadd(hour, (abs(checksum(@guidvue)) % ( + datediff(hour, @dtmmax, @dtmmin))), @dtmmin);
 end
 go 

调用以上函数的t-sql代码如下:

 select dbo.ufn_randdate('--', '--') as randdate, dbo.ufn_randdate('--', '--') as randdate;
 go 

执行后的查询结果如下:

 

 区间随机数获取的扩展思考

    无论是获取数值区间内的随机数值,还是日期区间内的随机日期,我们都使用了视图vrandomguid。如果有一个连续的数值序列,我们通过查询这个数值序列,结合我们的区间数值和随机排序,也可以实现以上的效果。

    假设该数值序列为seqdatatable(num int),数值区间[@intmin,@intmax],那么该数值区间任意一个随机数值的t-sql代码如下:

 select top num
 from dbo.seqdatatable
 where num between @intmin and @intmax
 order by newid() asc; 

    当然也可以利用以上的数字序列seqdatatable(num int),日期区间[@dtmmin,@dtmmax],那个该数字区间任意一个随机日期的t-sql代码如下:

 select top dateadd(day, num, @dtmmin)
 from dbo.seqdatatable
 where num between and datediff(day, @dtmmax, @dtmmin)
 order by newid() asc; 

 注意:以上代码无法执行成功的,也算是伪代码的。

(0)
上一篇 2022年3月21日
下一篇 2022年3月21日

相关推荐