从 SQL Server 中的十进制中删除尾随零
Posted
技术标签:
【中文标题】从 SQL Server 中的十进制中删除尾随零【英文标题】:Remove trailing zeros from decimal in SQL Server 【发布时间】:2011-02-25 16:01:57 【问题描述】:我有一列 DECIMAL(9,6)
,即它支持像 999,123456 这样的值。
但是当我插入像 123,4567 这样的数据时,它变成了 123,456700
如何去除那些零?
【问题讨论】:
请将正确答案更改为@Andomar 的答案 @dangalg:我坚信这确实应该在表示层中完成。这就是为什么我从来没有改变接受的答案。但是经过进一步考虑,我认为我应该接受社区明确指出的最佳答案。 【参考方案1】:我不愿意转换为浮点数,因为我的十进制中的位数可能比浮点数可以表示的多
FORMAT
与标准 .net 格式字符串 'g8' 一起使用时,在非常小的小数(例如 1e-08)也不合适的情况下返回科学记数法
使用自定义格式字符串 (https://docs.microsoft.com/en-us/dotnet/standard/base-types/custom-numeric-format-strings) 可以实现我想要的:
DECLARE @n DECIMAL(9,6) =1.23;
SELECT @n
--> 1.230000
SELECT FORMAT(@n, '0.######')
--> 1.23
如果您希望您的号码至少有一个尾随零,因此 2.0 不会变成 2,请使用格式字符串,如 0.0#####
小数点已本地化,因此使用逗号作为小数分隔符的区域性将遇到.
所在的逗号输出
当然,这是让数据层进行格式化的令人沮丧的做法(但在我的情况下,没有其他层;用户实际上是在运行存储过程并将结果放入电子邮件中:/)
【讨论】:
是的,这是唯一允许完全控制小数分隔符右侧的数字,同时避免使用科学记数法的解决方案。这个解决方案的唯一问题是 FORMAT 真的(!)慢。 (仍然……在 SQL2019 上)【参考方案2】:在上述所有答案中,我发现只有两个真正有效。不要转换为浮点数,在这里证明如果你这样做 3.175 不起作用。
SELECT round(cast(3.175 as float), 2) as roundingcheck,
CASE WHEN
round(cast(3.175 as float), 2) = 3.18 THEN 'PASSED' ELSE 'FAILED' END
as roundecheck,
CAST(round(TRY_CONVERT(DECIMAL(28,2), cast(3.175 as
float)), 2) as nvarchar(max)) as roundconvert,
round(CAST(3.175 as DECIMAL(18,10)), 4) as check1,
cast(CAST(round(CAST(3.175 as DECIMAL(18,10)), 4) as decimal(18,2)) as nvarchar(max)) as
roundconvert2,
cast(CAST(CAST(3.175 as DECIMAL(18,10)) as decimal(18,2)) as nvarchar(max)) as roundconvert3,
cast(CAST(CAST(3.149 as DECIMAL(18,10)) as decimal(18,1)) as nvarchar(max)) as roundconvert4,
cast(FORMAT(round(CAST(3.175 as
DECIMAL(18,10)), 2), '0.######') as nvarchar(max)) as roundconvert5
结果:
3.17 FAILED 3.17 3.1750000000 3.18 3.18 3.1 3.18
工作答案:
如果你想同时四舍五入到小数点后两位,使用这个:
cast(CAST(CAST(3.175 as DECIMAL(18,10)) as decimal(18,2)) as nvarchar(max)) as roundconvert3,
如果您不知道会有多少小数,您可以使用它并在需要时将其与 round 一起使用:
cast(FORMAT(round(CAST(3.175 as DECIMAL(18,10)), 2), '0.######') as nvarchar(max))
【讨论】:
【参考方案3】:DECIMAL(9,6) 列将转换为浮点数而不会损失精度,因此 CAST(... AS float) 可以解决问题。
@HLGEM:说浮点数是存储数字的糟糕选择,“从不使用浮点数”是不正确的——你只需要知道你的数字,例如温度测量值可以很好地作为浮点数。
@abatishchev 和@japongskie:如果不需要的话,在 SQL 存储过程和函数前面加上前缀仍然是个好主意;您提到的链接仅指示不要将“sp_”前缀用于您不应该使用的存储过程,其他前缀也可以,例如“usp_”或“spBob_”
参考: “所有具有 6 个或更少有效十进制数字的整数都可以转换为 IEEE 754 浮点值而不会损失精度”:https://en.wikipedia.org/wiki/Single-precision_floating-point_format
【讨论】:
【参考方案4】:您可以使用FORMAT()
函数(SqlAzure 和 Sql Server 2012+):
SELECT FORMAT(CAST(15.12 AS DECIMAL(9,6)), 'g18') -- '15.12'
SELECT FORMAT(CAST(0.0001575 AS DECIMAL(9,6)), 'g10') -- '0.000158'
SELECT FORMAT(CAST(2.0 AS DECIMAL(9,6)), 'g15') -- '2'
与 FLOAT(或 REAL)一起使用时要小心:不要使用 g17
或更大(或g8
或更大与 REAL),因为机器表示的有限精度会导致不良影响:
SELECT FORMAT(CAST(15.12 AS FLOAT), 'g17') -- '15.119999999999999'
SELECT FORMAT(CAST(0.9 AS REAL), 'g8') -- '0.89999998'
SELECT FORMAT(CAST(0.9 AS REAL), 'g7') -- '0.9'
此外,请注意,根据documentation:
FORMAT 依赖于 .NET Framework 公共语言的存在 运行时 (CLR)。此功能不会被远程控制,因为它取决于 CLR 的存在。远程处理需要 CLR 的函数 会导致远程服务器出错。
也适用于 SqlAzure。
【讨论】:
出于我的目的,我发现一个 g8 格式字符串将我的号码格式化为“1e-08”,这不是我想要的。这个答案确实让我找到了一个我可以使用的答案【参考方案5】:我遇到了类似的问题,需要从像 xx0000,x00000,xxx000
这样的数字中删除尾随零
我用过:
select LEFT(code,LEN(code)+1 - PATINDEX('%[1-Z]%',REVERSE(code))) from Tablename
代码是带有要修剪数字的字段的名称。希望这对其他人有帮助。
【讨论】:
当您使用的 SQL Server 版本太旧以至于您无法在 SQL Server 中使用 TRIM 或 FORMAT 内置函数时,此注释很有效。 我确实发现了这个答案的一个问题。如果“代码”字段中的值类似于“10”,那么它将返回“1”。如果数字是“10.00”,我认为它也会忽略小数点。【参考方案6】:最简单的方法是将值转换为 FLOAT,然后转换为字符串数据类型。
CAST(CAST(123.456000 AS FLOAT) AS VARCHAR(100))
【讨论】:
【参考方案7】:试试这个:
select isnull(cast(floor(replace(rtrim(ltrim('999,999.0000')),',','')) as int),0)
【讨论】:
这个返回999999
,OP要求删除尾随零。【参考方案8】:
试试这个。
select CAST(123.456700 as float),cast(cast(123.4567 as DECIMAL(9,6)) as float)
【讨论】:
【参考方案9】:SELECT CONVERT(DOUBLE PRECISION, [ColumnName])
【讨论】:
SQL Server 2008 及以上版本 当数字像“123.10705000000”时会发生什么?我尝试使用 SELECT CONVERT(DOUBLE PRECISION,123.10705000000) 但它给了我“123.107”作为答案。我想要“123.10705”作为输出?有什么办法吗?我不想使用 CHARINDEX。【参考方案10】:我知道这个线程已经很老了,但是对于那些不使用 SQL Server 2012 或更高版本或由于任何原因无法使用 FORMAT 函数的人,那么以下工作。
此外,如果数字小于 1(例如 0.01230000),很多解决方案都不起作用。
请注意,以下不适用于负数。
DECLARE @num decimal(28,14) = 10.012345000
SELECT PARSENAME(@num,2) + REPLACE(RTRIM(LTRIM(REPLACE(@num-PARSENAME(@num,2),'0',' '))),' ','0')
set @num = 0.0123450000
SELECT PARSENAME(@num,2) + REPLACE(RTRIM(LTRIM(REPLACE(@num-PARSENAME(@num,2),'0',' '))),' ','0')
分别返回 10.012345 和 0.012345。
【讨论】:
【参考方案11】:最好的方法是在转换之前不要转换为 FLOAT 或 MONEY,因为可能会丢失精度。所以安全的方法可以是这样的:
CREATE FUNCTION [dbo].[fn_ConvertToString]
(
@value sql_variant
)
RETURNS varchar(max)
AS
BEGIN
declare @x varchar(max)
set @x= reverse(replace(ltrim(reverse(replace(convert(varchar(max) , @value),'0',' '))),' ',0))
--remove "unneeded "dot" if any
set @x = Replace(RTRIM(Replace(@x,'.',' ')),' ' ,'.')
return @x
END
其中@value 可以是任意小数(x,y)
【讨论】:
@abatishchev,fn_ 是 sql 函数的首选前缀,前缀用于标准编码和命名约定以及最佳实践,我们可以自定义前缀,但对于最佳实践,我们使用sp_
对于存储过程,fn_
用于函数,tbl
用于表等等......这不是必需的,但这是组织数据库的最佳实践。
@japongskie:抱歉,没有。根本不需要前缀。这实际上是最糟糕的做法。见msdn.microsoft.com/en-us/library/dd172115(v=vs.100).aspxsqlperformance.com/2012/10/t-sql-queries/sp_prefixdba.stackexchange.com/q/25348/3186等等
@abatishchev,哦,我明白了.. 所以我不会再跟着学校的教学了.. 哈哈大声笑,因为你的链接来自 mdsn,谢谢这个,我现在将改变我的最佳实践.. :)【参考方案12】:
我知道这是一篇旧帖子,但想提供我想出的 SQL
DECLARE @value DECIMAL(23,3)
set @value = 1.2000
select @value original_val,
SUBSTRING( CAST( @value as VARCHAR(100)),
0,
PATINDEX('%.%',CAST(@value as VARCHAR(100)))
)
+ CASE WHEN ROUND(
REVERSE( SUBSTRING( CAST(@value as VARCHAR(100)),
PATINDEX('%.%',CAST(@value as VARCHAR(100)))+1,
LEN(CAST(@value as VARCHAR(100)))
)
)
,1) > 0 THEN
'.'
+ REVERSE(ROUND(REVERSE(SUBSTRING( CAST(@value as VARCHAR(100)),
PATINDEX('%.%',CAST(@value as VARCHAR(100)))+1,
LEN(CAST(@value as VARCHAR(100)))
)
),1))
ELSE '' END AS modified_val
【讨论】:
【参考方案13】:SELECT REVERSE(ROUND(REVERSE(2.5500),1))
打印:
2.55
【讨论】:
这个方法很好,如果只有零,它会留下一个尾随零。所以 2.5500 返回 2.55,而 2.000 返回 2.0 而不是 2。非常适合在车辆中格式化发动机尺寸时... @MasonG.Zhwiti 我怀疑这是否适用于小数点后有更多数字的小数,例如 232.33220003200 :-) @gotqn 好点,那肯定会失败。但是,对于我们的特定用例(在汽车中格式化引擎大小),它可以完美运行。 :) 正如@gotqn 所说.. 数字长时会出现问题 这不适用于像 0.56000 这样的数字。它将产生 56。有趣【参考方案14】:我遇到了类似的问题,但还需要删除不存在小数的小数点,这是我的解决方案,它将小数拆分为其组件,并将其从小数点字符串中获取的字符数基于小数部分的长度(不使用 CASE)。更有趣的是,我的数字被存储为不带小数的浮点数。
DECLARE @MyNum FLOAT
SET @MyNum = 700000
SELECT CAST(PARSENAME(CONVERT(NUMERIC(15,2),@MyNum/10000),2) AS VARCHAR(10))
+ SUBSTRING('.',1,LEN(REPLACE(RTRIM(REPLACE(CAST(PARSENAME(CONVERT(NUMERIC(15,2),@MyNum/10000),1) AS VARCHAR(2)),'0',' ')),' ','0')))
+ REPLACE(RTRIM(REPLACE(CAST(PARSENAME(CONVERT(NUMERIC(15,2),@MyNum/10000),1) AS VARCHAR(2)),'0',' ')),' ','0')
结果很痛苦,我知道,但在上述答案的帮助下,我成功了。
【讨论】:
【参考方案15】:Cast(20.5500 as Decimal(6,2))
应该这样做。
【讨论】:
【参考方案16】:试试这个:
SELECT REPLACE(TRIM(REPLACE(20.5500, "0", " ")), " ", "0")
给 20.55
【讨论】:
REPLACE(TRIM(REPLACE(20.00, "0", " ")), " ", "0") 给你留下一个尾随。 => “20。” 它完全适用于我的情况,似乎是最简单的解决方案。点赞并投票! 很好的解决方案,无需转换为浮点数!发现0.000
的一个小问题变成了.
。这是修复 SELECT REPLACE(RTRIM(REPLACE(20.5500, "0", " ")), " ", "0")
仅修剪尾随零
这是为了去掉点:SELECT REPLACE(RTRIM(REPLACE(REPLACE(RTRIM(REPLACE(25.00, '0', ' ')), ' ', '0'),'.',' ')),' ','.')
【参考方案17】:
case when left(replace(ltrim(rtrim(replace(str(XXX, 38, 10), '0', ' '))), ' ', '0'), 1) = '.'
then '0'
else ''
end +
replace(ltrim(rtrim(replace(str(XXX, 38, 10), '0', ' '))), ' ', '0') +
case when right(replace(ltrim(rtrim(replace(str(XXX, 38, 10), '0', ' '))), ' ', '0'), 1) = '.'
then '0'
else ''
end
【讨论】:
这不是也替换了数字之间的 0(零)吗?不要认为替换只是在末端起作用......【参考方案18】:可以在 TSQL 中删除前导零和尾随零
如果不是字符串,则使用STR TSQL函数将其转换为字符串,然后
删除前导零和尾随零
SELECT REPLACE(RTRIM(LTRIM(REPLACE(AccNo,'0',' '))),' ','0') AccNo FROM @BankAccount
更多信息请访问forum。
【讨论】:
有点丑但是这个版本杀掉了剩下的'.':REPLACE(RTRIM(REPLACE(REPLACE(RTRIM(REPLACE(X,'0',' ')),' ','0'),'.',' ')),' ','.')
小心,如果数字不是十进制,它也会修剪零。 CHARINDEX('.',@Number) != 1 将对此进行测试。
我之前的十进制检查是错误的。这里更好: Select Len(@Test) - Len(Replace(@Test, 'a', '')) As NumberOfCharacters Explained: tinyurl.com/o4fc8g7 and tinyurl.com/kgzkuqk【参考方案19】:
这个怎么样?假设数据以@thisData 的形式进入您的函数:
BEGIN
DECLARE @thisText VARCHAR(255)
SET @thisText = REPLACE(RTRIM(REPLACE(@thisData, '0', ' ')), ' ', '0')
IF SUBSTRING(@thisText, LEN(@thisText), 1) = '.'
RETURN STUFF(@thisText, LEN(@thisText), 1, '')
RETURN @thisText
END
【讨论】:
【参考方案20】:我需要删除小数点上的尾随零,这样我就可以输出一个只有 前导零的特定长度的字符串
(例如,我需要输出 14 个字符,以便 142.023400 变为 000000142.0234),
我使用parsename
、reverse
和cast
as int
删除了尾随零:
SELECT
PARSENAME(2.5500,2)
+ '.'
+ REVERSE(CAST(REVERSE(PARSENAME(2.5500,1)) as int))
(为了得到我的前导零,我可以根据上面的长度复制正确数量的零,并将其连接到上面的前面)
我希望这对某人有所帮助。
【讨论】:
@Protiguous 因为有 1 个小数点,所以 2.5500 的读法类似于另一种选择...
我不知道这有多有效,但它似乎工作并且不通过浮动:
select replace(rtrim(replace(
replace(rtrim(replace(cast(@value as varchar(40)), '0', ' ')), ' ', '0')
, '.', ' ')), ' ', '.')
中间的行去掉尾随的空格,如果没有小数位,外面的两行去掉点
【讨论】:
【参考方案22】:试试这个:
select Cast( Cast( (ROUND( 35.457514 , 2) *100) as Int) as float ) /100
【讨论】:
我更喜欢@user1959416 的答案,因为它不会改变值。例如,从 2.5550 开始,您的方法结果为 2.56,而他们的方法返回 2.555。【参考方案23】:decimal(9,6)
在逗号右侧存储 6 位数字。是否显示尾随零是格式决定,通常在客户端实现。
但由于 SSMS 格式 float
没有尾随零,您可以通过将 decimal
转换为 float
来删除尾随零:
select
cast(123.4567 as DECIMAL(9,6))
, cast(cast(123.4567 as DECIMAL(9,6)) as float)
打印:
123.456700 123,4567
(我的小数分隔符是逗号,但 SSMS 用点格式化十进制。显然是 known issue。)
【讨论】:
+1 我认为转换为浮点数会给结果带来一些不精确,但它似乎工作得很好。 这种方法的一个缺点是如果你从“2.0”开始,它会变成“2”。对于提出问题的人来说,这可能没问题,但我需要能够在小数点后保留一个零,而不保留任何其他尾随零。 @user1959416 的回答解决了这个问题。 Plus float 通常是存储数字的一个非常糟糕的选择。你会得到舍入错误,因为它不是一个精确的类型。永远不要使用浮动。 关于浮点数被格式化而没有尾随零的评论非常有用 我的想法是否正确,尽管小数的比例和精度可能超过浮点数,因此在某些情况下(有效位超过 17 位)这个答案不锻炼了吗?以上是关于从 SQL Server 中的十进制中删除尾随零的主要内容,如果未能解决你的问题,请参考以下文章
SQL Server:如何从字符串中删除前导/尾随非字母数字字符?