Удаление конечных нулей из десятичной дроби в SQL Server


85

У меня есть столбец, DECIMAL(9,6)т.е. он поддерживает такие значения, как 999,123456.

Но когда я вставляю данные типа 123,4567, они становятся 123,456700

Как убрать эти нули?


Пожалуйста, измените правильный ответ на ответ @ Andomar
dangalg

@dangalg: Я твердо верю, что это действительно нужно делать на уровне представления. Вот почему я так и не изменил принятый ответ. Однако после дальнейших размышлений я думаю, что должен принять то, что четко указало сообщество, какой ответ является лучшим.
Абатищев

Ответы:


147

A 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 форматирует десятичное число с точкой. Очевидно, это известная проблема .)


8
+1 Я думал, что преобразование в float внесет некоторую неточность в результаты, но, похоже, все работает нормально.
Мартин Смит

1
Одним из недостатков этого метода является то, что если вы начнете с «2.0», он превратится в «2». Вероятно, это нормально для человека, задающего вопрос, но мне нужно было сохранить один ноль после десятичной дроби, не оставляя никаких других конечных нулей. Ответ @ user1959416 решает это.
Мейсон Дж. Жвити

6
Плюс float в целом - очень плохой выбор для хранения чисел. Вы получите ошибки округления, так как это не точный тип. Никогда не используйте поплавок.
HLGEM 07

Комментарий о форматировании чисел с плавающей запятой без конечных нулей был чрезвычайно полезен
Санджив Дживан

Правильно ли я думаю, что масштаб и точность десятичной дроби могут превышать масштаб и точность числа с плавающей запятой, и, следовательно, могут быть случаи, когда (со значащими более 17 цифрами) этот ответ не сработает?
Caius Jard

32

Вы можете использовать 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'

Кроме того, обратите внимание, что согласно документации :

FORMAT зависит от наличия среды CLR .NET Framework. Эта функция не будет удалена, так как она зависит от наличия среды CLR. Удаление функции, для которой требуется CLR, вызовет ошибку на удаленном сервере.

Также работает в SqlAzure.


Для моих целей я нашел строку формата g8, отформатировавшую мое число как «1e-08», что было не тем, что мне было нужно. Этот ответ действительно привел меня к тому, что я мог бы использовать,
Кайус Джард

18
SELECT CONVERT(DOUBLE PRECISION, [ColumnName])

SQL Server 2008 и более
поздние версии

что происходит, когда число выглядит как «123.10705000000»? Я пробовал с помощью SELECT CONVERT (DOUBLE PRECISION, 123.10705000000), но он дает мне «123.107» в качестве ответа. и я хочу, чтобы на выходе было "123.10705"? Там в любом случае? Я не хочу использовать CHARINDEX.
Бхавика Зимбар

16

Я не хотел приводить к типу float из-за того, что в моей десятичной дроби может быть больше цифр, чем может представлять float

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)
Tor

7
SELECT REVERSE(ROUND(REVERSE(2.5500),1))

печатает:

2.55

2
Этот метод хорош тем, что он оставит конечный ноль, если есть только ноль. Таким образом, 2.5500 возвращает 2.55, а 2.000 возвращает 2.0, а не 2. Отлично подходит, когда вы форматируете размеры двигателя в автомобиле ...
Мейсон Дж. Жвити,

4
@ MasonG.Zhwiti Я сомневаюсь, что это будет работать с некоторыми десятичными числами с большим количеством цифр после десятичной точки, например, 232.33220003200: -)
gotqn

@gotqn Хорошее замечание, это определенно не работает. Однако для нашего конкретного случая использования (форматирование размеров двигателей в автомобилях) он отлично работает. :)
Мейсон Г. Жвити

как сказал @gotqn .. он глючит, когда номер длинный
Ofear

Это не совсем подходит для чисел вроде 0,56000. это даст 56. Смешно
Gunjan Shakya


4

Попробуй это :

SELECT REPLACE(TRIM(REPLACE(20.5500, "0", " ")), " ", "0")

Дает 20,55


1
REPLACE (TRIM (REPLACE (20.00, «0», «»)), «», «0») оставляет вас с завершением. => «20.»
Кейт Сирмонс 08

Это прекрасно применимо к моему случаю и кажется самым простым решением. Большой палец и голосование!
Oak_3260548

Отличное решение без преобразования в float! одна небольшая проблема , пятнистый 0.000стал .. вот исправление SELECT REPLACE(RTRIM(REPLACE(20.5500, "0", " ")), " ", "0")для обрезки только конечных нулей
Уилсон

2

У меня была аналогичная проблема, но мне также требовалось удалить десятичную точку, где десятичная дробь не присутствовала, вот мое решение, которое разбивает десятичную дробь на ее компоненты и основывает количество символов, которое он берет из строки десятичной точки, на длине компонент фракции (без использования 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') 

Я знаю, что результат болезненный, но я добился этого, благодаря приведенным выше ответам.


2

Лучший способ - НЕ преобразовывать в 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для таблиц и т. д. на ... это не требование, но это лучший способ организации наших баз данных.
japzdivino

@japongskie: извините, но нет. В префиксах вообще нет необходимости. На самом деле это худшая практика. См. Msdn.microsoft.com/en-us/library/dd172115(v=vs.100).aspx sqlperformance.com/2012/10/t-sql-queries/sp_prefix dba.stackexchange.com/q/25348/3186 и еще много
абатищев

@abatishchev, о, я понимаю ... так что я больше не буду следить за преподаванием в школе ... ха-ха LOL, раз уж твоя ссылка пришла с mdsn, спасибо за это, теперь я изменю свою лучшую практику .. :)
japzdivino

2

У меня была аналогичная проблема, мне нужно было обрезать конечные нули из чисел вроде xx0000,x00000,xxx000

Я использовал:

select LEFT(code,LEN(code)+1 - PATINDEX('%[1-Z]%',REVERSE(code))) from Tablename

Код - это имя поля, номер которого нужно обрезать. Надеюсь, это поможет кому-то другому.


Этот комментарий хорошо работает, когда вы используете версию SQL Server, достаточно старую, чтобы вы не могли использовать функции сборки TRIM или FORMAT в SQL Server.
Дэвид Парвин

Я нашел одну проблему с этим ответом. Если значение в поле «код» примерно равно «10», оно вернет «1». Если число равно «10,00», я думаю, он также игнорирует десятичную точку.
Дэвид Парвин

1

Другой вариант...

Я не знаю, насколько это эффективно, но похоже, что он работает и не проходит через float:

select replace(rtrim(replace(
       replace(rtrim(replace(cast(@value as varchar(40)), '0', ' ')), ' ', '0')
       , '.', ' ')), ' ', '.')

Средняя линия удаляет конечные пробелы, две внешние удаляют точку, если нет десятичных цифр.


1

Мне нужно было удалить конечные нули в моих десятичных числах, чтобы я мог вывести строку определенной длины только с ведущими нулями

(например, мне нужно было вывести 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 читается как <schema_name>. <object_name>. Второй параметр из 2 возвращает имя схемы, второй параметр из 1 возвращает имя объекта. Обычно он используется как PARSENAME ('dbo.TableName', 2) для возврата dbo или PARSENAME ('dbo.TableName', 1) для возврата TableName.
Али

Здравствуйте .. Я вижу, что мой псевдоним отмечен в вашем комментарии. Я не знаю почему?
Protiguous

Точный вопрос, который вы задали мне 26 мая 2020 года, звучал так: «Как здесь должен работать PARSENAME ???» Извините, что так долго вам ответили.
Али

1

в TSQL можно убрать начальные и конечные нули

  1. Преобразуйте его в строку, используя функцию STR TSQL, если не строку, Затем

  2. Удалите начальные и конечные нули

    SELECT REPLACE(RTRIM(LTRIM(REPLACE(AccNo,'0',' '))),' ','0') AccNo FROM @BankAccount
    
  3. Больше информации на форуме .


4
Немного некрасиво, но эта версия убивает остатки »:REPLACE(RTRIM(REPLACE(REPLACE(RTRIM(REPLACE(X,'0',' ')),' ','0'),'.',' ')),' ','.')
Chris B

1
Будьте осторожны, если число не является десятичным, оно также обрежет нули. CHARINDEX ('.', @ Number)! = 1 проверит это.
Muflix

Моя предыдущая десятичная проверка неверна. Вот что лучше: выберите Len (@Test) - Len (Replace (@Test, 'a', '')) Как объяснено NumberOfCharacters: tinyurl.com/o4fc8g7 и tinyurl.com/kgzkuqk
Muflix

0

Как насчет этого? Предполагая, что данные поступают в вашу функцию как @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

0
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

Разве это не заменит также нули между числами? Не думайте, что замена работает только на концах ...
mtk

0

Я понимаю, что это старый пост, но хотел бы предоставить 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


0

Самый простой способ - преобразовать значение как FLOAT, а затем в строковый тип данных.

CAST(CAST(123.456000 AS FLOAT) AS VARCHAR(100))

-1

Попробуй это:

select Cast( Cast( (ROUND( 35.457514 , 2) *100) as Int) as float ) /100

Я предпочитаю ответ @ user1959416 здесь лучше, поскольку он не меняет значения. Например, начиная с 2,5550, ваш метод дает 2,56, а их метод возвращает 2,555.
Мейсон Дж. Жвити

-1

Я знаю, что этот поток очень старый, но для тех, кто не использует 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 соответственно.


-1

Попробуй это:

select isnull(cast(floor(replace(rtrim(ltrim('999,999.0000')),',','')) as int),0)

этот возвращается 999999, OP просит удалить конечные нули.
japzdivino

-1

Столбец DECIMAL (9,6) преобразуется в число с плавающей запятой без потери точности, поэтому CAST (... AS float) поможет.


@HLGEM: сказать, что float - плохой выбор для хранения чисел, а «Никогда не использовать float» неверно - вам просто нужно знать свои числа, например, измерения температуры будут хорошо проходить как поплавки.

@abatishchev и @japongskie: префиксы перед сохраненными процедурами и функциями SQL по-прежнему являются хорошей идеей, если не требуются; упомянутые вами ссылки только инструктируют не использовать префикс «sp_» для хранимых процедур, которые вы не должны использовать, другие префиксы подходят, например, «usp_» или «spBob_»

Ссылка: «Все целые числа с 6 или менее значащими десятичными знаками могут быть преобразованы в значение с плавающей запятой IEEE 754 без потери точности»: https://en.wikipedia.org/wiki/Single-precision_floating-point_format

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.