Rimuovere gli zeri finali dai decimali in SQL Server


85

Ho una colonna, DECIMAL(9,6)ovvero supporta valori come 999,123456.

Ma quando inserisco dati come 123,4567 diventa 123,456700

Come rimuovere quegli zeri?


Si prega di cambiare la risposta corretta alla risposta di @ Andomar
dangalg

@dangalg: credo fermamente che questo debba essere fatto nel livello di presentazione. Ecco perché non ho mai cambiato la risposta accettata. Tuttavia, dopo ulteriori considerazioni, penso che dovrei accettare ciò che la comunità ha chiaramente indicato quale risposta è la migliore.
abatishchev

Risposte:


147

A decimal(9,6)memorizza 6 cifre a destra della virgola. Se visualizzare o meno gli zeri finali è una decisione di formattazione, solitamente implementata dal lato client.

Ma poiché SSMS formatta floatsenza zeri finali, è possibile rimuovere gli zeri finali eseguendo il cast decimaldi a float:

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

stampe:

123.456700  123,4567

(Il mio separatore decimale è una virgola, ma SSMS formatta il decimale con un punto. Apparentemente un problema noto .)


8
+1 Pensavo che la conversione in float avrebbe introdotto qualche imprecisione nei risultati ma sembra funzionare perfettamente.
Martin Smith

1
Uno svantaggio di questo metodo è che se inizi con "2.0" lo trasformerà in "2". Questo probabilmente è OK per la persona che pone la domanda, ma dovevo essere in grado di mantenere un singolo zero dopo il decimale, senza mantenere altri zeri finali. La risposta di @ user1959416 lo risolve.
Mason G. Zhwiti

6
Plus float in generale è una scelta molto scarsa per memorizzare i numeri. Otterrai errori di arrotondamento poiché non è un tipo esatto. Non usare mai il galleggiante.
HLGEM

Il commento sui float formattati senza zeri finali è stato estremamente utile
Sanjiv Jivan

Ho ragione nel pensare però che la scala e la precisione di un decimale possono superare quella di un float e quindi potrebbero esserci casi in cui (con un significativo di oltre 17 cifre) questa risposta non funziona?
Caio Jard

32

È possibile utilizzare la FORMAT()funzione (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'

Fare attenzione quando si utilizza con FLOAT (o REAL): non utilizzare g17o maggiore (o g8o maggiore con REAL), perché la limitata precisione della rappresentazione della macchina provoca effetti indesiderati:

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'

Si noti inoltre che, secondo la documentazione :

FORMAT si basa sulla presenza di .NET Framework Common Language Runtime (CLR). Questa funzione non verrà remotata poiché dipende dalla presenza del CLR. Il remotaggio di una funzione che richiede CLR provocherebbe un errore sul server remoto.

Funziona anche in SqlAzure.


Per i miei scopi, ho trovato una stringa di formato g8 che ha formattato il mio numero come "1e-08" che non era quello che stavo cercando. Questa risposta mi ha portato a uno che potrei usare però
Caius Jard

18
SELECT CONVERT(DOUBLE PRECISION, [ColumnName])

SQL Server 2008 e versioni successive
Bat_Programmer

cosa succede quando il numero è come "123.10705000000"? Ho provato con SELECT CONVERT (DOUBLE PRECISION, 123.10705000000) ma mi dà "123.107" come risposta. e voglio "123.10705" come output? C'è un modo? Non voglio usare CHARINDEX.
Bhavika Zimbar

16

Ero riluttante a lanciare in float a causa della possibilità che più cifre siano nel mio decimale di quanto il float possa rappresentare

FORMAT se usato con una stringa di formato .net standard 'g8' restituiva la notazione scientifica in casi di decimali molto piccoli (es. 1e-08) che era anche inadatta

L'utilizzo di una stringa di formato personalizzata ( https://docs.microsoft.com/en-us/dotnet/standard/base-types/custom-numeric-format-strings ) mi ha permesso di ottenere ciò che volevo:

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

Se vuoi che il tuo numero abbia almeno uno zero finale, quindi 2.0 non diventa 2, usa una stringa di formato come 0.0#####

Il punto decimale è localizzato, quindi le culture che usano una virgola come separatore decimale incontreranno un output di virgola in cui il. è

Naturalmente, questa è la pratica scoraggiante di avere il livello dati che esegue la formattazione (ma nel mio caso non c'è nessun altro livello; l'utente sta letteralmente eseguendo una procedura memorizzata e inserendo il risultato in un'e-mail: /)


Sì, questa è l'unica soluzione che consente il pieno controllo sui numeri a destra del separatore decimale, evitando la notazione scientifica. L'unico problema con questa soluzione è che FORMAT è veramente (!) Lento. (Ancora .. su SQL2019)
Tor

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

stampe:

2.55

2
Questo metodo è carino in quanto lascerà uno zero finale se c'è solo uno zero. Quindi 2.5500 restituisce 2,55 e 2.000 restituisce 2,0 anziché 2. Ottimo per quando si formattano le dimensioni del motore di un veicolo ...
Mason G. Zhwiti

4
@ MasonG.Zhwiti Ho dei dubbi che funzionerà con alcuni decimali con più cifre dopo il punto decimale come 232.33220003200 per esempio: -)
gotqn

@ Gotqn Buon punto, sicuramente fallisce. Tuttavia, per il nostro caso d'uso specifico (formattazione delle dimensioni del motore nelle auto), funziona perfettamente. :)
Mason G. Zhwiti

come ha detto @gotqn .. è bacato quando il numero è lungo
Ofear

Questo non funziona esattamente per numeri come 0,56000. ne produrrà 56. Divertente
Gunjan Shakya


4

Prova questo :

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

Dà 20,55


1
REPLACE (TRIM (REPLACE (20.00, "0", "")), "", "0") ti lascia con un finale. => "20."
Keith Sirmons

È perfettamente applicabile al mio caso e sembra essere la soluzione più semplice. Pollice e vota!
Oak_3260548

Ottima soluzione senza dover convertire in float! un problema minore individuato è 0.000diventato .. ecco la correzione SELECT REPLACE(RTRIM(REPLACE(20.5500, "0", " ")), " ", "0")per tagliare solo gli zeri finali
wilson

2

Ho avuto un problema simile, ma è stato anche richiesto di rimuovere il punto decimale dove non era presente alcun decimale, ecco la mia soluzione che divide il decimale nei suoi componenti e basa il numero di caratteri che prende dalla stringa del punto decimale sulla lunghezza di il componente frazione (senza utilizzare CASE). Per rendere le cose ancora più interessanti, il mio numero è stato memorizzato come un galleggiante senza i suoi decimali.

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

Il risultato è doloroso, lo so, ma ci sono arrivato, con molto aiuto dalle risposte sopra.


2

Il modo migliore è NON convertire in FLOAT o MONEY prima della conversione a causa della possibilità di perdita di precisione. Quindi i modi sicuri possono essere qualcosa del genere:

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

dove @value può essere qualsiasi decimale (x, y)


@abatishchev, fn_ è un prefisso preferito per le funzioni sql, i prefissi vengono utilizzati nella codifica standard e nella convenzione di denominazione nonché nelle migliori pratiche, possiamo personalizzare i nostri prefissi, ma per le migliori pratiche che usiamo sp_per le procedure memorizzate, fn_per le funzioni, tblper le tabelle e così via su ... questo non è un requisito ma questa è la migliore pratica per organizzare i nostri database.
japzdivino

@japongskie: scusa, ma no. Non c'è assolutamente bisogno di prefissi. Questa è in realtà la peggiore pratica. Vedere 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 molti altri
abatishchev

@abatishchev, oh capisco .. quindi non seguirò più l'insegnamento della scuola .. haha ​​LOL, dal momento che il tuo link proviene da mdsn, grazie per questo, cambierò ora la mia migliore pratica .. :)
japzdivino

2

Ho avuto un problema simile, dovevo tagliare gli zeri finali da numeri come xx0000,x00000,xxx000

Ero solito:

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

Codice è il nome del campo con il numero da tagliare. Spero che questo aiuti qualcun altro.


Questo commento funziona bene quando si utilizza una versione di SQL Server sufficientemente vecchia da non poter utilizzare le funzioni di compilazione TRIM o FORMAT in SQL Server.
David Parvin

Ho trovato un problema con questa risposta. Se il valore nel campo "codice" è qualcosa come "10", restituirà "1". Se il numero è "10 .00 ", penso che ignori anche il punto decimale.
David Parvin,

1

Un'altra opzione...

Non so quanto sia efficiente ma sembra funzionare e non funziona tramite float:

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

La linea centrale rimuove gli spazi finali, i due esterni rimuovono il punto se non ci sono cifre decimali


1

Ho bisogno di rimuovere zeri sui miei decimali trascinamento così ho potuto uscita una stringa di una determinata lunghezza con solo leader zeri

(es. dovevo produrre 14 caratteri in modo che 142.023400 diventasse 000000142.0234),

Ho usato parsename, reversee cast as intper rimuovere gli zeri:

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

(Per ottenere i miei zeri iniziali potrei replicare il numero corretto di zeri in base alla lunghezza di quanto sopra e concatenarlo davanti a quanto sopra)

Spero che questo aiuti qualcuno.


@Protiguous Poiché c'è 1 punto decimale, 2.5500 viene letto come <schema_name>. <object_name>. Il secondo parametro di 2 restituisce il nome dello schema, il secondo parametro di 1 restituisce il nome dell'oggetto. Di solito viene utilizzato come PARSENAME ('dbo.TableName', 2) per restituire dbo o PARSENAME ('dbo.TableName', 1) per restituire TableName.
Ali

Ciao .. vedo che il mio alias è contrassegnato nel tuo commento. Non so perché?
Protiguo

La domanda esatta che mi hai posto il 26 maggio 2020 è stata "Come dovrebbe funzionare PARSENAME qui ???" Mi dispiace che ci sia voluto così tanto tempo per risponderti.
Ali

1

è possibile rimuovere gli zeri iniziali e finali in TSQL

  1. Convertirlo in stringa utilizzando la funzione STR TSQL se non stringa, quindi

  2. Rimuovi entrambi gli zeri iniziali e finali

    SELECT REPLACE(RTRIM(LTRIM(REPLACE(AccNo,'0',' '))),' ','0') AccNo FROM @BankAccount
    
  3. Maggiori informazioni sul forum .


4
Un po 'brutta, ma questa versione uccide gli avanzi ".":REPLACE(RTRIM(REPLACE(REPLACE(RTRIM(REPLACE(X,'0',' ')),' ','0'),'.',' ')),' ','.')
Chris B

1
Fai attenzione, se il numero non è decimale, taglierà anche gli zeri. CHARINDEX ('.', @ Number)! = 1 lo verificherà.
Muflix

Il mio precedente controllo decimale è sbagliato. Qui è meglio: Seleziona Len (@Test) - Len (Sostituisci (@Test, 'a', '')) Come spiegato da NumberOfCharacters: tinyurl.com/o4fc8g7 e tinyurl.com/kgzkuqk
Muflix

0

Cosa ne pensi di questo? Supponendo che i dati entrino nella tua funzione come @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

Questo non sostituirà anche gli 0 (zeri) tra i numeri? Non pensare che la sostituzione funzioni solo alle estremità ...
mtk

0

Capisco che questo sia un vecchio post, ma vorrei fornire SQL che mi è venuto in mente

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

prova questo.

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

0

Il modo più semplice consiste nel CAST il valore come FLOAT e quindi in un tipo di dati stringa.

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

-1

Prova questo:

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

Preferisco la risposta di @ user1959416 meglio qui, poiché non cambia i valori. Ad esempio, a partire da 2,5550, il tuo metodo restituisce 2,56, mentre il loro restituisce 2,555.
Mason G. Zhwiti

-1

So che questo thread è molto vecchio ma per coloro che non utilizzano SQL Server 2012 o versioni successive o non possono utilizzare la funzione FORMAT per qualsiasi motivo, il seguente funziona.

Inoltre, molte soluzioni non funzionavano se il numero era inferiore a 1 (ad es. 0,01230000).

Tieni presente che quanto segue non funziona con i numeri negativi.

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

Restituisce rispettivamente 10,012345 e 0,012345.


-1

Prova questo:

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

questo ritorna 999999, l'OP chiede di rimuovere gli zeri finali.
japzdivino

-1

Una colonna DECIMAL (9,6) verrà convertita in float senza perdita di precisione, quindi CAST (... AS float) farà il trucco.


@HLGEM: dire che il float è una scelta sbagliata per memorizzare i numeri e "Non usare mai il float" non è corretto - devi solo conoscere i tuoi numeri, ad esempio le misurazioni della temperatura andrebbero bene come galleggianti.

@abatishchev e @japongskie: i prefissi davanti alle procedure e alle funzioni archiviate in SQL sono ancora una buona idea, se non richieste; i collegamenti che hai menzionato indicano solo di non utilizzare il prefisso "sp_" per le procedure memorizzate che non dovresti usare, altri prefissi vanno bene ad esempio "usp_" o "spBob_"

Riferimento: "Tutti i numeri interi con 6 o meno cifre decimali significative possono essere convertiti in un valore a virgola mobile IEEE 754 senza perdita di precisione": https://en.wikipedia.org/wiki/Single-precision_floating-point_format

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.