不能直接用 RAND() 生成券码,因其在单条语句内为常量,同一语句多次调用返回相同值,且无法保证并发安全;可靠方案须依赖唯一约束+重试机制或预生成池,而非随机拼接。

为什么不能直接用 RAND() 生成券码?
直接在循环里调用 RAND() 生成字符串,大概率会撞码——因为 RAND() 在单条语句内是常量(尤其在 INSERT ... SELECT 场景下),同一语句中多次调用返回相同值;即使加 NEWID() 或时间因子,也难保并发插入时不重复。
真正可靠的去重不是靠“运气”,而是靠唯一约束 + 重试机制或预生成池。
- 别依赖
RAND()+ 字符拼接直接入库,它不解决并发冲突 -
NEWID()可用于生成 UUID 片段,但长度和可读性通常不符合业务券码要求(比如要 8 位大写字母+数字) - 如果表没建
UNIQUE索引,批量插入重复码后才发现,清洗成本远高于预防
怎么用存储过程实现「生成→校验→入库」闭环?
核心逻辑是:每次生成一个候选码 → 尝试插入 → 插入失败(违反唯一约束)就重试,直到成功。这不是低效,而是最可控的方式。
示例结构(SQL Server):
CREATE PROCEDURE GenerateVoucherCodes
@count INT,
@length INT = 8
AS
BEGIN
DECLARE @i INT = 0;
WHILE @i BEGIN TRY
INSERT INTO Vouchers(Code, CreatedAt) VALUES (@code, GETDATE());
SET @i = @i + 1;
END TRY
BEGIN CATCH
IF ERROR_NUMBER() = 2627 OR ERROR_NUMBER() = 2601 -- 唯一约束冲突
CONTINUE; -- 重试
ELSE
THROW; -- 其他错误抛出
END CATCH
END
END
- 必须提前在
Vouchers.Code上建UNIQUE索引,否则无法触发 2627 错误 -
dbo.GenerateRandomCode函数建议用SUBSTRING(CONVERT(VARCHAR(36), NEWID()), 1, @length)+ 字符映射(过滤掉 `-` 和 `0OIl` 等易混淆字符) - 避免用
WHILE做几万次循环——实际生产建议分批(如每次 1000 码),防止长时间阻塞
大批量场景下,为什么推荐「预生成池 + 消费式取码」?
当需要一次性生成 10 万+ 券码,或高并发发码(比如秒杀),逐个重试的存储过程会变慢且不稳定。这时应把生成和发放解耦。
做法:先用集合操作批量生成大量候选码(带去重),存入临时表或专用池表,再通过 UPDATE ... OUTPUT 原子化消费:
-- 预生成 10 万候选码(利用递归 CTE + CHECKSUM(NEWID()) 避免 RAND 同值)
WITH nums AS (
SELECT TOP 100000 ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) n
FROM sys.columns a, sys.columns b
)
SELECT DISTINCT TOP 100000
UPPER(CONVERT(CHAR(8), HASHBYTES('SHA2_256', CAST(n AS VARCHAR)+CAST(CHECKSUM(NEWID()) AS VARCHAR)), 2))
FROM nums
-- 截取/映射为 8 位字母数字,再 INSERT INTO VoucherPool (Code, Status) WHERE Status = 'pending'
- 用
DISTINCT+ 大量源数据,比单条重试快得多;但要注意HASHBYTES输出含小写字母和符号,需二次清洗 - 发码时用
UPDATE TOP(1) VoucherPool SET Status='used' OUTPUT INSERTED.Code WHERE Status='pending',保证线程安全 - 池表必须有
UNIQUE(Code)和INDEX ON Status,否则消费性能暴跌
容易被忽略的三个细节
很多方案跑通了但上线就翻车,问题往往卡在这三处:
- 没处理大小写混淆:用户输入
IO1L和系统生成的10il被当成不同码,实际应统一转为大写并剔除0O1I - 没设最大重试次数:万一池快耗尽,无限重试会导致存储过程卡死,应在循环里加计数器和超时判断
- 事务隔离级别影响:默认
READ COMMITTED下,并发插入仍可能因间隙锁漏判重复,必要时改用SNAPSHOT或应用层加分布式锁
券码不是 ID,它要能被人抄、能被扫、能被客服核对——所有技术选择得先过这一关。










