为何向表值函数传入REPLICATE(NEWID())时,NEWID会被重复计算?
问题:表值函数传入
REPLICATE(NEWID(),2)时出现异常结果 我编写了如下dbo.chunk表值函数,用于将字符串按指定长度拆分,测试时发现两种调用方式结果不一致:
函数代码
CREATE FUNCTION dbo.chunk(@input VARCHAR(MAX), @chunkSize INT = 36) RETURNS TABLE AS RETURN ( WITH CTE AS ( SELECT SUBSTRING(@input,1,@chunkSize) AS [chunk], 1 AS [row] UNION ALL SELECT SUBSTRING(@input,1+([row]*@chunkSize),@chunkSize) , [row] + 1 FROM cte WHERE LEN(@input) > ([row]*@chunkSize) ) SELECT [chunk] FROM cte ) GO
测试代码
/* 以下执行结果符合预期 */ DECLARE @input varchar(MAX) = REPLICATE(NEWID(),2); SELECT * FROM dbo.chunk(@input,36) /* 但以下执行结果异常,REPLICATE调用了两次NEWID() */ SELECT * FROM dbo.chunk(REPLICATE(NEWID(),2),36);
测试结果
第一种调用方式(传变量)结果:
| chunk |
|---|
| E1810B3D-3DD4-4F55-B650-ED2DB28BCF70 |
| E1810B3D-3DD4-4F55-B650-ED2DB28BCF70 |
第二种调用方式(直接传表达式)结果:
| chunk |
|---|
| 89A26C8B-D5C7-47A8-BBBC-FE859B24E267 |
| F9636F76-1ED6-4D19-A309-BA35EAC9F782 |
原因解释
核心问题出在SQL Server对非确定性函数的求值逻辑上:
NEWID()属于非确定性函数,这类函数每次被调用时都会生成新的结果,不会复用之前的值。- 第一种调用方式中,
REPLICATE(NEWID(),2)先赋值给变量@input,此时NEWID()仅执行一次,变量存储了两个相同GUID拼接的固定字符串。函数递归拆分时,所有引用都是基于这个固定字符串,所以两个chunk内容一致。 - 第二种调用方式中,
REPLICATE(NEWID(),2)直接作为参数传入函数。由于函数内部的递归CTE会在锚点查询和递归查询中两次引用@input参数,每次引用都会重新计算这个表达式,导致NEWID()被执行两次,生成两个不同的GUID,最终两个chunk内容不同。
简单来说,非确定性函数作为参数直接传入递归表值函数时,会随着函数内部的多次引用被反复求值,从而出现不符合预期的结果。
内容的提问来源于stack exchange,提问作者user19463405
相关产品推荐
相关产品推荐

