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?

Abatishchev
fonte
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 .)

Andomar
fonte
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.

robert4
fonte
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])
Bat_Programmer
fonte
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: /)

Caius Jard
fonte
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
user1959416
fonte
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
5
Cast(20.5500 as Decimal(6,2))

deve fazer isso.

Peter Jones
fonte
4

Experimente isto:

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

Dá 20,55

Todd
fonte
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.

Adge Cutler
fonte
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)

Mahmoud Moravej
fonte
@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.

Kazeem Muritala
fonte
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

SQLian
fonte
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.

Todos
fonte
@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 .

Ishtiaq
fonte
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
MrNazgul
fonte
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
beloblotskiy
fonte
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
Abhi
fonte
0

tente isso.

select CAST(123.456700 as float),cast(cast(123.4567 as DECIMAL(9,6)) as float)
Vishal Kiri
fonte
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))
Brad Raiche
fonte
-1

Experimente isto:

select Cast( Cast( (ROUND( 35.457514 , 2) *100) as Int) as float ) /100
Vipin JS
fonte
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.

Alan Schofield
fonte
-1

Experimente isto:

select isnull(cast(floor(replace(rtrim(ltrim('999,999.0000')),',','')) as int),0)
Kierzo
fonte
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

Kobus
fonte