创建一个在SQL Server触发器中生成随机数的函数

时间:2015-08-01 11:52:04

标签: sql sql-server triggers

我必须在SQL Server触发器中创建一个函数,用于在插入后生成随机数。我想用生成的随机数更新列,请帮助我在代码中遗漏的内容。

如果您了解其他方式,请提出完成任务的方法。

这是我的SQL Server触发器:

ALTER TRIGGER [dbo].[trgEnquiryMaster]
ON [dbo].[enquiry_master]
AFTER INSERT 
AS 
    declare @EnquiryId int;
    declare @ReferenceNo varchar(50);
    declare @GenReferenceNo NVARCHAR(MAX);

    select @EnquiryId = i.enquiry_id from inserted i;
    select @ReferenceNo = i.reference_no from inserted i;
BEGIN
     SET @GenReferenceNo = 'CREATE FUNCTION functionRandom (@Reference VARCHAR(MAX) )
        RETURNS VARCHAR(MAX)
        As
        Begin
        DECLARE @r varchar(8);
        SELECT @r = coalesce(@r, '') + n
        FROM (SELECT top 8 
        CHAR(number) n FROM
        master..spt_values
        WHERE type = P AND 
        (number between ascii(0) and ascii(9)
        or number between ascii(A) and ascii(Z)
        or number between ascii(a) and ascii(z))
        ORDER BY newid()) a

        RETURNS @r
        END
        '

        EXEC(@GenReferenceNo)

    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON

    -- update statements for trigger here
    UPDATE enquiry_master 
    SET reference_no ='updated' 
    WHERE enquiry_id = @EnquiryId
END   

2 个答案:

答案 0 :(得分:3)

触发器应该灵活且快速 - 它不能进行繁重且耗时的处理,并且绝对没有地方可以创建新的数据库对象,因为(a)触发器在引发它的代码的上下文,以及(b)你无法控制触发器被触发的时间和频率。

你需要

  1. 定义并创建您的函数,以便在数据库设置期间生成随机值 - 一次,在对数据库执行任何操作之前

  2. 重写您的触发器以考虑可以一次插入多行,在这种情况下,Inserted表将包含多行,这些行都必须是处理。

  3. 所以你的触发器看起来像这样(我有几个假设 - 例如enquiry_id是你桌子上的主键 - 你需要这个来建立数据表和你之间的INNER JOIN Inserted伪表:

    ALTER TRIGGER [dbo].[trgEnquiryMaster]
    ON [dbo].[enquiry_master]
    AFTER INSERT 
    AS 
        -- SET NOCOUNT ON added to prevent extra result sets from
        -- interfering with SELECT statements.
        SET NOCOUNT ON
    
        -- update statements for trigger here
        UPDATE enq
        SET reference_no = dbo.GenerateRandomValue(.....)
        FROM enquiry_master enq
        INNER JOIN inserted i ON enq.enquiry_id = i.enquiry_id  
    

答案 1 :(得分:3)

要生成随机数,只需调用SQL Server 2008中引入的CRYPT_GEN_RANDOM

SELECT CRYPT_GEN_RANDOM(5) AS [Hex],
       CONVERT(VARCHAR(20), CRYPT_GEN_RANDOM(5), 2) AS [HexStringWithout0x],
       CONVERT(VARCHAR(20), CRYPT_GEN_RANDOM(10)) AS [Translated-ASCII],
       CONVERT(NVARCHAR(20), CRYPT_GEN_RANDOM(20)) AS [Translated-UCS2orUTF16]

返回:

Hex             HexStringWithout0x    Translated-ASCII    Translated-UCS2orUTF16
0x4F7D9ABBC4    0ECF378A7A            ¿"bü<ݱØï           붻槬㟰添䛺⯣왚꒣찭퓚

如果你只有0 - 9和A - F,那么CONVERT(VARCHAR(20), CRYPT_GEN_RANDOM(5), 2)就是你所需要的。

有关详细信息,请参阅DBA.StackExchange上有关类似问题的答案:

Password generator function

该链接答案的“更新”部分中显示的UPDATE语句是您想要的,只需删除WHERE条件并将JOIN添加到Inserted即可伪表。

查询应如下所示:

DECLARE @Length INT = 10;

UPDATE em
SET    em.[reference_no] = rnd.RandomValue
FROM   dbo.enquiry_master em
INNER JOIN Inserted ins
                 ON ins.enquiry_id = em.enquiry_id
CROSS APPLY dbo.GenerateReferenceNo(CRYPT_GEN_RANDOM((em.[enquiry_id] % 1) + @Length)) rnd;

由于功能略有不同,以下是为了获得大写和小写字母的方式:

CREATE FUNCTION dbo.GenerateReferenceNo(@RandomValue VARBINARY(20))
RETURNS TABLE
WITH SCHEMABINDING
AS RETURN
  WITH base(item) AS
  (
    SELECT NULL UNION ALL SELECT NULL UNION ALL SELECT NULL UNION ALL
    SELECT NULL UNION ALL SELECT NULL UNION ALL SELECT NULL
  ), items(item) AS
  (
    SELECT NULL
    FROM   base b1
    CROSS JOIN base b2
  )
  SELECT (
     SELECT TOP (LEN(@RandomValue))
            SUBSTRING('1234567890QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm',
                      (CONVERT(TINYINT, SUBSTRING(@RandomValue, 1, 1)) % 62) + 1,
                      1) AS [text()]
     FROM   items
     FOR XML PATH('')
  ) AS [RandomReferenceNo];
GO

并且按照上面显示的用法,传递CRYPT_GEN_RANDOM((em.[enquiry_id] % 1) + @Length)CRYPT_GEN_RANDOM(@RefferenceNOLength)

其他说明:

  • @marc_s已经解释了一行与多行的缺陷以及如何解决这个问题。
  • 不仅触发器不是创建新对象(即函数)的地方,因为调用newid()(在ORDER BY中)不是,所以该函数无论如何都不起作用在一个函数中允许。
  • 您无需发出两个单独的SELECT来设置两个不同的变量。您可以执行以下操作:

    SELECT @EnquiryId = i.enquiry_id,
           @ReferenceNo = i.reference_no
    FROM   TableName i;
    
  • 将字符串传递给函数需要在单引号内引用这些字符串:ASCII('A')而不是ASCII(A)

<强>更新

完整的触发器定义应如下所示:

ALTER TRIGGER [dbo].[trgEnquiryMaster]
ON [dbo].[enquiry_master]
AFTER INSERT
AS
BEGIN
  DECLARE @Length INT = 10;

  UPDATE em
  SET    em.[reference_no] = rnd.RandomValue
  FROM   dbo.enquiry_master em
  INNER JOIN Inserted ins
          ON ins.enquiry_id = em.enquiry_id
  CROSS APPLY dbo.GenerateReferenceNo(
                                       CRYPT_GEN_RANDOM((em.[enquiry_id] % 1) + @Length)
                                     ) rnd;
END;