【问题标题】:SQL Cursor to generate unique non sequencial PINSSQL 游标生成唯一的非顺序 PINS
【发布时间】:2017-02-23 04:58:40
【问题描述】:

我正在尝试为一群新员工生成非连续的唯一 PIN。 以下是它的工作原理:

  1. 游标应该从表 B 中获取所有新员工
  2. 为每位新员工生成新的独特别针
  3. 检查新生成的PIN是否已经存在于表A中
  4. 如果是,请重复第 2 步,直到我们为所有新员工获得唯一的 PIN 码
  5. 将新员工编号和 PIN 码插入表 A

在表 A 上,字段为 PIN、Employee_Number、Date

我尝试使用 cursor 一个一个地插入每个新的 emp 和他的 PIN,以便在每次插入时检查唯一性:

这只会循环一次并停止,有人可以告诉我我错过了什么

 DECLARE 
    @EmpNo             VARCHAR(50), 
    @Pin               INT, 
    @today             DATETIME,
    @Upper             INT,
    @Lower             INT,
SET @Lower = 100000 ---- The lowest random number allowed
SET @Upper = 999999 ---- The highest random number allowed

DECLARE cur CURSOR FOR 

SELECT   
       Employee_Number, 
       (ROUND(((@Upper - @Lower -1) * RAND() + @Lower), 0))as PIN, 
       GETDATE()
FROM     TableB 
WHERE    Employee_Number NOT IN (SELECT Employee_Number FROM TableA)


OPEN cur
FETCH NEXT FROM cur INTO @EmpNo, @Pin, @Today

WHILE @@FETCH_STATUS=0

BEGIN    

         INSERT INTO TableA (PIN, Employee_Number, Date)
         SELECT @Pin, @EmpNo, @Today


FETCH NEXT FROM cur INTO @Pin, @EmpNo, @Today
END

CLOSE cur
DEALLOCATE cur

【问题讨论】:

    标签: sql-server loops random cursor unique


    【解决方案1】:

    你不需要光标。另外最好使用 newid() 来生成随机数,而不是 rand()

    INSERT INTO TableA (PIN, Employee_Number, Date)
    SELECT pin = @Lower 
               + (   abs(convert(bigint,convert(varbinary(20),newid()))) 
                   % (@Upper - @Lower + 1) 
                 ),
           Employee_Number,
           GETDATE()
    FROM   TableB 
    WHERE  Employee_Number NOT IN (SELECT Employee_Number FROM TableA)
    

    编辑:

    要处理唯一的 pin,不使用光标的解决方案是保留所有可能 pin 的表。

    create table pin
    (
        pin         int primary key,
        is_used     bit default 0
    )
    

    您使用递归 cte 生成 pin

    ; with num as
    (
        -- anchor member
        select  pin = @Lower
    
        union all
    
        -- recursive member
        select  pin = pin + 1
        from    num
        where   pin < @Upper
    )
    insert into pin (pin) 
    select  pin
    from    num
    

    那么当您需要将 pin 分配给员工时

    ; with emp as
    (
        select  Employee_Number, rn = row_number() over (order by Employee_Number)
        from    TableA a
        where   not exists
                (
                    select  *
                    from    TableB b
                    where   b.Employee_Number   = a.Employee_Number
                )
    ),
    pin as
    (
        -- get unused pin, order it randomly using newid()
        select  pin, rn = row_number() over (order by newid())
        from    pin
        where   is_used = 0
    )
    insert into #TableB (Employee_Number, pin)
    select  e.Employee_Number, p.pin
    from    emp e
            inner join pin p    on  e.rn    = p.rn
    

    在 pin 表中更新 is_used 列之后

    update  p
    set     is_used = 1
    from    TableB b
            inner join #pin p   on  b.pin   = p.pin
    where   p.is_used   = 0
    

    【讨论】:

    • 感谢您的选择。此查询不会生成任何重复的 PIN,但会引发 UNIQUE KEY 约束错误,因为表 a 上的 pin 字段设置为 UNIQUE。从 pin 字段中删除这个独特的关键功能是否安全?
    • 并且在插入时,查询还应该检查表 A 任何重复的引脚
    • 为什么 PIN 在员工中必须是唯一的?对我来说真的没有意义
    • 独特的 pin 将使查询复杂化。但是,为了确保唯一的 pin,您可以考虑在 pin 列中使用 IDENTITY。
    • @AhmedSaeed,这不是 PIN sequential 吗?
    【解决方案2】:

    另一种方法可能如下。

    注意@EmpTable 是一个示范性的 EmpTable(为您的环境删除 @)

    1) cte0 生成一个 10 行的基本计数表

    2) cteR 将 cte0 扩展为 1000 行并添加一个随机字符串 (NewID())

    3) cteI 将 cteR 转换为 INT(不超过 6 位)

    4) 更新适用于所有 NULL PINS

    Declare @EmpTable table (Employee_Number int,PIN int)
    Insert Into @EmpTable values
    (1,100288),(2,651701),(3,823700),
    (4,NULL),(5,NULL)
    
    Declare @Lower int=100000,@Upper int = 999999
    
     ;with cte0(N)   As (Select 1 From (Values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N(N))
         , cteR(R)   As (Select Distinct R=cast(NewID() as varchar(50)) From cte0 N1, cte0 N2, cte0 N3, cte0 N4) 
         , cteI(R,N) As (Select Distinct R,cast(Left(cast(abs(cast(Hashbytes('MD5',R) as int)) as varchar(25)),6) as int) From cteR R) 
    Update @EmpTable Set PIN = B.N
     From  @EmpTable A
     Join  (
             Select RowNr=(Select min(Employee_Number)-1 From @EmpTable Where PIN is NULL)+Row_Number() over (Order By R)
                   ,N 
              From  cteI 
              Where N Between @Lower and @Upper
                and N not in (Select Distinct PIN From @EmpTable Where PIN is not NULL)
            ) B on A.Employee_Number=B.RowNr and PIN is null
    
    
    Select * from @EmpTable
    

    更新了 EmpTable

    Employee_Number PIN
    1               100288   -- Same as Original
    2               651701   -- Same as Original
    3               823700   -- Same as Original
    4               118844   -- New Random PIN
    5               677855   -- New Random PIN
    

    【讨论】:

      猜你喜欢
      • 2015-10-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-11-28
      相关资源
      最近更新 更多