使用SQL标量值函数拼接结果仅返回最后一条数据的问题
问题根源
在SQL Server里,用SELECT @变量 = @变量 + 表达式这种方式累加字符串,微软官方并没有保证这种行为的确定性。尤其是调用标量值函数的时候,查询优化器可能会调整执行逻辑:先批量算出所有行的函数结果,最后只把最后一条的值赋值给变量,而非逐行叠加。这就是为啥你用内联计算能正常拼接,一调用标量函数就只拿到最后一条结果的原因。
解决办法
给你几个靠谱的实现方案:
1. 使用STRING_AGG(SQL Server 2017及以上版本)
这是官方推荐的字符串聚合方法,语法简洁且行为稳定:
DECLARE @Columns NVARCHAR(MAX); SELECT @Columns = STRING_AGG([dbo].[getName]([Name], [DeletedDate], DEFAULT), ',') WITHIN GROUP (ORDER BY [Order] DESC) FROM [dbo].[Items] WHERE [ProjectId] = 1; SELECT @Columns;
2. 使用FOR XML PATH(兼容SQL Server 2016及更早版本)
如果你的SQL Server版本较低,用这种经典写法也能实现需求:
DECLARE @Columns NVARCHAR(MAX); SELECT @Columns = STUFF( (SELECT ',' + [dbo].[getName]([Name], [DeletedDate], DEFAULT) FROM [dbo].[Items] WHERE [ProjectId] = 1 ORDER BY [Order] DESC FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '' ); SELECT @Columns;
3. 将标量函数改为内联表值函数(可选优化)
标量值函数性能普遍较差,还容易触发执行计划异常。改成内联表值函数不仅能解决当前问题,还能提升整体性能:
CREATE OR ALTER FUNCTION getName_ITVF ( @name NVARCHAR(200), @deletedDate DATETIME2(7) = NULL, @suffix NVARCHAR(50) = NULL ) RETURNS TABLE AS RETURN SELECT QUOTENAME(CONCAT(@name, IIF(@suffix IS NOT NULL, ' ' + @suffix, ''), IIF(@deletedDate IS NOT NULL, CONCAT(' (DELETED - ', FORMAT(@deletedDate, 'dd.MM.yyyy HH:mm:ss'), ')'), ''))) AS Result;
之后用这个函数进行拼接:
DECLARE @Columns NVARCHAR(MAX); SELECT @Columns = STRING_AGG(f.Result, ',') WITHIN GROUP (ORDER BY [Order] DESC) FROM [dbo].[Items] i CROSS APPLY [dbo].[getName_ITVF](i.[Name], i.[DeletedDate], DEFAULT) f WHERE i.[ProjectId] = 1; SELECT @Columns;
内容的提问来源于stack exchange,提问作者Sergiu Molnar
相关产品推荐
相关产品推荐

