实现可用于UPDATE脚本的“获取或生成存储并返回数据”功能
解决方案:实现可用于UPDATE JOIN/APPLY的"获取或生成"函数
我明白你的核心需求了——需要一个能直接嵌在UPDATE的JOIN/APPLY操作里的工具,逻辑是"查到已生成数据就返回,查不到就生成、存储后再返回"。你现有的存储过程没法直接满足这个场景(SQL Server不允许在UPDATE的FROM/JOIN上下文里调用存储过程),所以我们把逻辑转换成表值函数,再搭配一个缓存表来实现需求。
步骤1:创建缓存生成数据的存储表
首先建一张表用来保存已生成的数据,确保同一个@orig值只会对应一条生成结果:
CREATE TABLE GeneratedDataStore ( OrigValue NVARCHAR(20) PRIMARY KEY, -- 用输入的@orig作为唯一键,避免重复生成 GeneratedValue NVARCHAR(20) NOT NULL, CreatedDateTime DATETIME2 DEFAULT GETUTCDATE() -- 可选:记录生成时间,方便排查 );
步骤2:实现表值版的"获取或生成"函数
这个函数会先检查缓存表中是否已有对应数据,没有的话就调用你现有的generateData()生成,存入缓存表后再返回结果。表值函数的优势就是可以直接在UPDATE的JOIN/APPLY中引用:
CREATE OR ALTER FUNCTION dbo.GetOrGenerateData(@orig NVARCHAR(20)) RETURNS @Result TABLE (GeneratedValue NVARCHAR(20)) AS BEGIN DECLARE @generatedVal NVARCHAR(20); -- 第一步:尝试从缓存表读取已存在的数据 SELECT @generatedVal = GeneratedValue FROM GeneratedDataStore WHERE OrigValue = @orig; -- 如果没找到,就生成并存储 IF @generatedVal IS NULL BEGIN -- 调用你现有的随机生成函数 SET @generatedVal = dbo.generateData(); -- 用MERGE而非直接INSERT,避免并发场景下的主键冲突 MERGE INTO GeneratedDataStore AS target USING (SELECT @orig AS OrigValue, @generatedVal AS GeneratedValue) AS source ON target.OrigValue = source.OrigValue WHEN NOT MATCHED THEN INSERT (OrigValue, GeneratedValue) VALUES (source.OrigValue, source.GeneratedValue); -- 再次读取确保拿到最新值(应对并发插入的极端情况) SELECT @generatedVal = GeneratedValue FROM GeneratedDataStore WHERE OrigValue = @orig; END -- 返回结果到表值函数的输出 INSERT INTO @Result (GeneratedValue) VALUES (@generatedVal); RETURN; END;
步骤3:在UPDATE的JOIN/APPLY中使用这个函数
现在你可以直接在UPDATE语句里用这个函数了,比如用CROSS APPLY(适合每行都需要生成/获取数据的场景):
-- 示例:更新目标表,将GeneratedColumn设置为对应的数据 UPDATE targetTable SET targetTable.GeneratedColumn = genData.GeneratedValue FROM YourTargetTable AS targetTable CROSS APPLY dbo.GetOrGenerateData(targetTable.YourOrigColumn) AS genData;
或者用JOIN的形式:
UPDATE targetTable SET targetTable.GeneratedColumn = genData.GeneratedValue FROM YourTargetTable AS targetTable JOIN dbo.GetOrGenerateData(targetTable.YourOrigColumn) AS genData ON 1=1;
关键注意事项
- 并发安全:用
MERGE语句而不是单纯的INSERT,是为了避免多个会话同时处理同一个@orig时触发主键冲突错误,MERGE是原子操作,能保证只会插入一条数据。 - 缓存重置:如果需要重新生成某个
@orig对应的数据,只需要删除GeneratedDataStore中对应的行,下次调用函数时就会重新生成并存储。 - 性能优化:
GeneratedDataStore的OrigValue已经设为主键,所以查询和匹配的速度会很快,即使数据量较大也能高效运行。 - 生成逻辑的随机性:因为你的
generateData()包含随机逻辑,但一旦生成后就会存在缓存表中,同一个@orig后续调用都会返回相同的结果,完全符合你"获取已生成数据"的需求。
内容的提问来源于stack exchange,提问作者MGorgon
相关产品推荐
相关产品推荐

