存储过程直接执行正常,赋值变量时返回值始终为0排查
问题
执行存储过程[dbo].[NumbersToWords]时遇到异常:单独执行该存储过程能返回正确的数字转英文结果,但使用exec @str = NumbersToWords @number = 1520123将返回值赋值给变量@str时,@str始终返回0。
存储过程代码
ALTER PROCEDURE [dbo].[NumbersToWords] @Number INT AS BEGIN SET NOCOUNT ON; SET @Number = ABS(@Number) DECLARE @vResult NVARCHAR(MAX) = '' -- 预定义数字映射表 DECLARE @tDict TABLE (Num INT NOT NULL, Nam NVARCHAR(255) NOT NULL) INSERT INTO @tDict (Num, Nam) VALUES (1,'one'),(2,'two'),(3,'three'),(4,'four'),(5,'five'),(6,'six'),(7,'seven'),(8,'eight'),(9,'nine'), (10,'ten'),(11,'eleven'),(12,'twelve'),(13,'thirteen'),(14,'fourteen'),(15,'fifteen'),(16,'sixteen'),(17,'seventeen'),(18,'eighteen'),(19,'nineteen'), (20,'twenty'),(30,'thirty'),(40,'fourty'),(50,'fifty'),(60,'sixty'),(70,'seventy'),(80,'eighty'),(90,'ninety') DECLARE @ZeroWord NVARCHAR(10) = 'zero' DECLARE @DotWord NVARCHAR(10) = 'point' DECLARE @AndWord NVARCHAR(10) = 'and' DECLARE @HundredWord NVARCHAR(10) = 'hundred' DECLARE @ThousandWord NVARCHAR(10) = 'thousand' DECLARE @MillionWord NVARCHAR(10) = 'million' DECLARE @BillionWord NVARCHAR(10) = 'billion' DECLARE @TrillionWord NVARCHAR(10) = 'trillion' -- 处理小数部分(注:@Number是INT类型,此处逻辑实际不会触发) DECLARE @vDecimalNum INT = (@Number - FLOOR(@Number)) * 100 DECLARE @vLoop SMALLINT = CONVERT(SMALLINT, SQL_VARIANT_PROPERTY(@Number, 'Scale')) DECLARE @vSubDecimalResult NVARCHAR(MAX) = N'' IF @vDecimalNum > 0 BEGIN WHILE @vLoop > 0 BEGIN IF @vDecimalNum % 10 = 0 SET @vSubDecimalResult = FORMATMESSAGE('%s %s', @ZeroWord, @vSubDecimalResult) ELSE SELECT @vSubDecimalResult = FORMATMESSAGE('%s %s', Nam, @vSubDecimalResult) FROM @tDict WHERE Num = @vDecimalNum%10 SET @vDecimalNum = FLOOR(@vDecimalNum/10) SET @vLoop = @vLoop - 1 END END -- 处理整数部分 SET @Number = FLOOR(@Number) IF @Number = 0 SET @vResult = @ZeroWord ELSE BEGIN DECLARE @vSubResult NVARCHAR(MAX) = '' DECLARE @v000Num DECIMAL(15,0) = 0 DECLARE @v00Num DECIMAL(15,0) = 0 DECLARE @v0Num DECIMAL(15,0) = 0 DECLARE @vIndex SMALLINT = 0 WHILE @Number > 0 BEGIN -- 从右往左取每三位数字 SET @v000Num = @Number % 1000 SET @v00Num = @v000Num % 100 SET @v0Num = @v00Num % 10 IF @v000Num = 0 BEGIN SET @vSubResult = '' END ELSE BEGIN -- 处理两位数字 IF @v00Num < 20 BEGIN -- 小于20的数字直接取映射 SELECT @vSubResult = Nam FROM @tDict WHERE Num = @v00Num IF @v00Num < 10 AND @v00Num > 0 AND (@v000Num > 99 OR FLOOR(@Number / 1000) > 0)--例如1001:一千和一;或201000:(二百和一)千 SET @vSubResult = FORMATMESSAGE('%s %s', @AndWord, @vSubResult) END ELSE BEGIN -- 大于等于20的数字拆分十位和个位 SELECT @vSubResult = Nam FROM @tDict WHERE Num = @v0Num SET @v00Num = FLOOR(@v00Num/10)*10 SELECT @vSubResult = FORMATMESSAGE('%s %s', Nam, @vSubResult) FROM @tDict WHERE Num = @v00Num END -- 处理百位数字 IF @v000Num > 99 SELECT @vSubResult = FORMATMESSAGE('%s %s %s', Nam, @HundredWord, @vSubResult) FROM @tDict WHERE Num = CONVERT(INT,@v000Num / 100) END -- 添加量级单位(千、百万等) IF @vSubResult <> '' BEGIN SET @vSubResult = FORMATMESSAGE('%s %s', @vSubResult, CASE WHEN @vIndex=1 THEN @ThousandWord WHEN @vIndex=2 THEN @MillionWord WHEN @vIndex=3 THEN @BillionWord WHEN @vIndex=4 THEN @TrillionWord WHEN @vIndex>3 AND @vIndex%3=2 THEN @MillionWord + ' ' + RTRIM(LTRIM(REPLICATE(@BillionWord + ' ',@vIndex%3))) WHEN @vIndex>3 AND @vIndex%3=0 THEN RTRIM(LTRIM(REPLICATE(@BillionWord + ' ',@vIndex%3))) ELSE '' END) SET @vResult = FORMATMESSAGE('%s %s', @vSubResult, @vResult) END -- 处理下一组三位数字(向左) SET @vIndex = @vIndex + 1 SET @Number = FLOOR(@Number / 1000) END END SET @vResult = FORMATMESSAGE('%s %s', RTRIM(LTRIM(@vResult)), COALESCE(@DotWord + ' ' + NULLIF(@vSubDecimalResult,''), '')) -- 返回结果 SELECT UPPER(@vResult) AS 'Result' END
测试情况
- 单独执行时结果正常:
declare @number INT = 1520123 exec NumbersToWords @number = 1520123
执行后会返回正确的英文数字字符串。
- 赋值给变量时返回0:
declare @number INT = 1520123 declare @str nvarchar(max) exec @str = NumbersToWords @number = 1520123 select @str as 'Word'
执行后@str的值始终为0。
原因与解决方法
原因
exec @变量 = 存储过程名这种语法获取的是存储过程的返回状态码,不是存储过程内部SELECT输出的结果。默认情况下,如果存储过程没有显式使用RETURN语句返回值,SQL Server会返回0表示执行成功,这就是@str始终为0的原因。
你的存储过程是通过SELECT UPPER(@vResult) AS 'Result'输出结果,而非通过返回值或输出参数传递,因此无法通过这种方式捕获结果。
解决方法
有两种常用方式可以捕获存储过程的输出结果:
方法1:修改存储过程,添加输出参数
修改存储过程定义,新增一个输出参数传递结果,替代原来的SELECT语句:
ALTER PROCEDURE [dbo].[NumbersToWords] @Number INT, @Result NVARCHAR(MAX) OUTPUT -- 新增输出参数 AS BEGIN SET NOCOUNT ON; -- 保留原有所有逻辑不变,直到最后一步 -- 替换原来的SELECT语句,将结果赋值给输出参数 SET @Result = UPPER(RTRIM(LTRIM(@vResult)) + ' ' + COALESCE(@DotWord + ' ' + NULLIF(@vSubDecimalResult,''), '')) END
调用时使用OUTPUT关键字获取结果:
declare @number INT = 1520123 declare @str nvarchar(max) exec NumbersToWords @number = 1520123, @Result = @str OUTPUT select @str as 'Word'
方法2:使用表变量/临时表捕获查询结果
如果不想修改存储过程,可以在调用时用表变量或临时表存储存储过程的输出:
declare @number INT = 1520123 declare @resultTable TABLE (Result NVARCHAR(MAX)) -- 定义表变量 declare @str nvarchar(max) -- 将存储过程的SELECT结果插入表变量 INSERT INTO @resultTable exec NumbersToWords @number = 1520123 -- 从表变量中取出结果赋值给@str SELECT @str = Result FROM @resultTable select @str as 'Word'
内容的提问来源于stack exchange,提问作者Ruchira
相关产品推荐
相关产品推荐

