Remova os zeros finais do decimal no SQL Server


85

Eu tenho uma coluna, DECIMAL(9,6)ou seja, ela oferece suporte a valores como 999,123456.

Mas quando eu insiro dados como 123,4567, ele se torna 123,456700

Como remover esses zeros?


Altere a resposta correta para a resposta de @ Andomar
— dangalg

@dangalg: Eu realmente acredito que isso deveria ser feito na camada de apresentação. É por isso que nunca mudei a resposta aceita. No entanto, após mais considerações, acho que devo aceitar o que a comunidade indicou claramente qual é a melhor resposta.
— abatishchev

Respostas:


147

A decimal(9,6)armazena 6 dígitos no lado direito da vírgula. A exibição de zeros à direita ou não é uma decisão de formatação, geralmente implementada no lado do cliente.

Mas, como o SSMS formata floatsem zeros à direita, você pode remover os zeros à direita convertendo o decimalpara um float:

select 
    cast(123.4567 as DECIMAL(9,6))
,   cast(cast(123.4567 as DECIMAL(9,6)) as float)

estampas:

123.456700  123,4567

(Meu separador decimal é uma vírgula, mas o SSMS formata decimal com um ponto. Aparentemente, um problema conhecido .)


8
1 Achei que a conversão para float iria introduzir alguma imprecisão nos resultados, mas parece funcionar perfeitamente bem.
— Martin Smith

1
Uma desvantagem desse método é que se você começar com "2.0", ele será transformado em "2". Isso provavelmente é bom para a pessoa que fez a pergunta, mas eu precisava ser capaz de manter um único zero após o decimal, sem manter quaisquer outros zeros à direita. A resposta de @ user1959416 resolve isso.
— Mason G. Zhwiti

6
Além disso, a flutuação em geral é uma escolha muito ruim para armazenar números. Você obterá erros de arredondamento, pois não é um tipo exato. Nunca use flutuador.
— HLGEM

O comentário sobre os flutuadores sendo formatados sem zeros à direita foi extremamente útil
— Sanjiv Jivan

Estou correto em pensar que a escala e a precisão de um decimal podem exceder a de um float e, portanto, pode haver casos em que (com um significativo de mais de 17 dígitos) essa resposta não funcione?
— Caius Jard de

32

Você pode usar a FORMAT()função (SqlAzure e 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'

Tenha cuidado ao usar com FLOAT (ou REAL): não use g17ou maior (ou g8ou maior com REAL), pois a precisão limitada da representação da máquina causa efeitos indesejados:

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'

Além disso, observe que, de acordo com a documentação :

FORMAT depende da presença do .NET Framework Common Language Runtime (CLR). Esta função não será remota, pois depende da presença do CLR. A comunicação remota de uma função que requer o CLR causaria um erro no servidor remoto.

Funciona no SqlAzure também.


Para meus propósitos, encontrei uma string de formato de g8 formatado meu número como "1e-08" que não era o que eu procurava. Essa resposta me levou a uma que eu poderia usar
— Caius Jard

18
SELECT CONVERT(DOUBLE PRECISION, [ColumnName])

SQL Server 2008 e superior
— Bat_Programmer

o que acontece quando o número é como "123.10705000000"? Eu tentei com SELECT CONVERT (DOUBLE PRECISION, 123.10705000000), mas me deu "123.107" como resposta. e eu quero "123.10705" como saída? Existe alguma maneira? Não quero usar o CHARINDEX.
— Bhavika Zimbar

16

Eu estava relutante em lançar para flutuar devido ao potencial de mais dígitos estarem no meu decimal do que o flutuante pode representar

FORMAT quando usado com uma string de formato .net padrão, 'g8' retornou a notação científica em casos de decimais muito pequenos (por exemplo, 1e-08), que também era inadequada

Usar uma string de formato personalizado ( https://docs.microsoft.com/en-us/dotnet/standard/base-types/custom-numeric-format-strings ) me permitiu alcançar o que eu queria:

DECLARE @n DECIMAL(9,6) =1.23;
SELECT @n
--> 1.230000
SELECT FORMAT(@n, '0.######')
--> 1.23

Se você quiser que seu número tenha pelo menos um zero à direita, para que 2.0 não se torne 2, use uma string de formato como 0.0#####

O ponto decimal é localizado, então as culturas que usam uma vírgula como separador decimal encontrarão uma saída de vírgula onde o. é

Claro, esta é a prática desencorajadora de ter a camada de dados fazendo a formatação (mas no meu caso não há outra camada; o usuário está literalmente executando um procedimento armazenado e colocando o resultado em um e-mail: /)


Sim, esta é a única solução que permite controle total sobre os números à direita do separador decimal, evitando a notação científica. O único problema com esta solução é que FORMAT é realmente (!) Lento. (Ainda .. no SQL2019)
— Tor

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

estampas:

2.55

2
Este método é bom porque deixará um zero à direita se houver apenas um zero. Portanto, 2.5500 retorna 2.55 e 2.000 retorna 2.0 em vez de 2. Ótimo para quando você formatar tamanhos de motor em um veículo ...
— Mason G. Zhwiti

4
@ MasonG.Zhwiti Eu tenho dúvidas de que isso funcionará com algum decimal com mais dígitos após o ponto decimal como 232.33220003200 por exemplo: -)
— gotqn

@gotqn Bom argumento, isso definitivamente falha. No entanto, para nosso caso de uso específico (formatação de tamanhos de motores em carros), ele funciona perfeitamente. :)
— Mason G. Zhwiti

como @gotqn disse .. é bugado quando o número é longo
— Ofear

Isso não funciona exatamente para números como 0,56000. ela renderá 56. Engraçado
— Gunjan Shakya


4

Experimente isto:

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

Dá 20,55


1
REPLACE (TRIM (REPLACE (20.00, "0", "")), "", "0") deixa você com um rastro. => "20"
— Keith Sirmons

É perfeitamente aplicável ao meu caso e parece ser a solução mais simples. Polegar e votar!
— Oak_3260548

Ótima solução sem ter que converter para flutuar! um problema menor detectado 0.000tornou - se .. aqui está a correção SELECT REPLACE(RTRIM(REPLACE(20.5500, "0", " ")), " ", "0")para cortar apenas zeros à direita
— wilson

2

Eu tive um problema semelhante, mas também fui obrigado a remover o ponto decimal onde nenhum decimal estava presente, aqui estava minha solução que divide o decimal em seus componentes e baseia o número de caracteres que leva da string do ponto decimal no comprimento de o componente da fração (sem usar CASE). Para tornar as coisas ainda mais interessantes, meu número foi armazenado como um flutuante sem seus decimais.

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

O resultado é doloroso, eu sei, mas cheguei lá, com muita ajuda das respostas acima.


2

A melhor maneira é NÃO converter para FLUTUANTE ou DINHEIRO antes da conversão devido à chance de perda de precisão. Portanto, as formas seguras podem ser mais ou menos assim:

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

onde @value pode ser qualquer decimal (x, y)


@abatishchev, fn_ é um prefixo preferido para funções sql, prefixos é usado na codificação padrão e na convenção de nomenclatura, bem como nas melhores práticas, podemos personalizar nossos prefixos, mas para as melhores práticas, usamos sp_para procedimento armazenado, fn_para funções, tblpara tabelas e assim on ... isso não é um requisito, mas são as melhores práticas para organizar nossos bancos de dados.
— japzdivino

@japongskie: desculpe, mas não. Não há necessidade de prefixos. Esta é realmente a pior prática. Consulte 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 e muitos mais
— abatishchev

@abatishchev, ah entendi .. então não vou seguir mais o ensino da escola .. haha ​​rsrs, já que seu link veio do mdsn, obrigado por este, vou mudar agora minha melhor prática .. :)
— japzdivino

2

Eu tive um problema semelhante, precisei cortar os zeros à direita de números como xx0000,x00000,xxx000

Eu usei:

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

Código é o nome do campo com o número a ser aparado. Espero que isso ajude mais alguém.


Este comentário funciona bem quando você está usando uma versão do SQL Server que é antiga o suficiente para que você não possa usar as funções de build in TRIM ou FORMAT no SQL Server.
— David Parvin

Eu encontrei um problema com esta resposta. Se o valor no campo 'código' for algo como '10', ele retornará '1'. Se o número for '10,00 ', acho que ele também ignora o ponto decimal.
— David Parvin

1

Outra opção...

Não sei o quão eficiente isso é, mas parece funcionar e não passa por flutuação:

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

A linha do meio remove os espaços finais, as duas externas removem o ponto se não houver dígitos decimais


1

Eu precisava remover zeros à direita nas minhas casas decimais para que eu pudesse mostrar uma string de um determinado comprimento com apenas líder zeros à

(por exemplo, eu precisava imprimir 14 caracteres para que 142.023400 se tornasse 000000142.0234),

Usei parsename, reversee cast as intpara remover os zeros finais:

SELECT
    PARSENAME(2.5500,2)
    + '.'
    + REVERSE(CAST(REVERSE(PARSENAME(2.5500,1)) as int))

(Para obter meus zeros à esquerda, eu poderia replicar o número correto de zeros com base no comprimento do acima e concatená-lo na frente do acima)

Espero que isso ajude alguém.


@Protiguous Uma vez que há 1 ponto decimal, 2.5500 é lido como <schema_name>. <object_name>. O segundo parâmetro de 2 retorna o nome do esquema, o segundo parâmetro de 1 retorna o nome do objeto. Normalmente seria usado como PARSENAME ('dbo.TableName', 2) para retornar dbo ou PARSENAME ('dbo.TableName', 1) para retornar TableName.
— Ali

Olá .. Vejo que meu alias está sinalizado em seu comentário. Eu não sei porque?
— Protíguo de

A pergunta exata que você me fez em 26 de maio de 2020 foi “Como o PARSENAME deveria funcionar aqui ???” Desculpe a demora para responder a você.
— Ali

1

é possível remover zeros à esquerda e à direita em TSQL

  1. Converta em string usando a função STR TSQL se não for string, então

  2. Remova os zeros à esquerda e à direita

    SELECT REPLACE(RTRIM(LTRIM(REPLACE(AccNo,'0',' '))),' ','0') AccNo FROM @BankAccount
    
  3. Mais informações no fórum .


4
Um pouco feia, mas essa versão mata as sobras '.':REPLACE(RTRIM(REPLACE(REPLACE(RTRIM(REPLACE(X,'0',' ')),' ','0'),'.',' ')),' ','.')
— Chris B

1
Cuidado, se o número não for decimal, ele também eliminará os zeros. CHARINDEX ('.', @ Number)! = 1 irá testar isso.
— Muflix

Minha verificação decimal anterior está errada. Aqui é melhor: Selecione Len (@Test) - Len (Substituir (@Test, 'a', '')) Como NumberOfCharacters explicado: tinyurl.com/o4fc8g7 e tinyurl.com/kgzkuqk
— Muflix

0

Que tal agora? Supondo que os dados entrem em sua função como @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

Isso também não substituiria os 0's (zeros) entre os números? Não pense que a substituição funciona apenas nas pontas ...
— mtk

0

Eu entendo que esta é uma postagem antiga, mas gostaria de fornecer o SQL que criei

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

tente isso.

select CAST(123.456700 as float),cast(cast(123.4567 as DECIMAL(9,6)) as float)

0

A maneira mais fácil é CAST o valor como FLOAT e então para um tipo de dados string.

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

-1

Experimente isto:

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

Prefiro a resposta de @ user1959416 melhor aqui, pois não altera os valores. Por exemplo, começando com 2,5550, seu método resulta em 2,56, enquanto o deles retorna 2,555.
— Mason G. Zhwiti

-1

Eu sei que este tópico é muito antigo, mas para aqueles que não usam o SQL Server 2012 ou superior ou não podem usar a função FORMAT por algum motivo, o seguinte funciona.

Além disso, muitas das soluções não funcionaram se o número fosse menor que 1 (por exemplo, 0,01230000).

Observe que o seguinte não funciona com números negativos.

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

Retorna 10.012345 e 0.012345 respectivamente.


-1

Experimente isto:

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

este retorna 999999, o OP está pedindo para remover os zeros à direita.
— japzdivino

-1

Uma coluna DECIMAL (9,6) será convertida em float sem perda de precisão, então CAST (... AS float) fará o truque.


@HLGEM: dizer que float é uma escolha ruim para armazenar números e "Nunca use float" não é correto - você só precisa saber seus números, por exemplo, medições de temperatura funcionariam bem como floats.

@abatishchev e @japongskie: prefixos na frente de procs e funções armazenados em SQL ainda são uma boa ideia, se não forem necessários; os links que você mencionou apenas instruem a não usar o prefixo "sp_" para procedimentos armazenados que você não deve usar; outros prefixos são adequados, por exemplo, "usp_" ou "spBob_"

Referência: "Todos os inteiros com 6 ou menos dígitos decimais significativos podem ser convertidos em um valor de ponto flutuante IEEE 754 sem perda de precisão": https://en.wikipedia.org/wiki/Single-precision_floating-point_format

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.