Usuń końcowe zera z dziesiętnych w programie SQL Server


85

Mam kolumnę DECIMAL(9,6)tj. Obsługuje wartości takie jak 999,123456.

Ale kiedy wstawię dane, takie jak 123,4567, staje się 123,456700

Jak usunąć te zera?


Zmień poprawną odpowiedź na odpowiedź @ Andomar
dangalg

@dangalg: Jestem przekonany, że rzeczywiście należy to zrobić w warstwie prezentacji. Dlatego nigdy nie zmieniłem zaakceptowanej odpowiedzi. Jednak po dalszych rozważaniach myślę, że powinienem zaakceptować to, co społeczność jasno wskazała, która odpowiedź jest najlepsza.
abatishchev

Odpowiedzi:


147

A decimal(9,6)przechowuje 6 cyfr po prawej stronie przecinka. To, czy wyświetlać zera końcowe, czy nie, jest decyzją o formatowaniu, zwykle wdrażaną po stronie klienta.

Ale ponieważ formatów SSMS floatbez zer można usunąć końcowe zera przez odlewanie decimalDo float:

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

wydruki:

123.456700  123,4567

(Mój separator dziesiętny to przecinek, ale SSMS formatuje po przecinku z kropką. Najwyraźniej znany problem .)


8
+1 Myślałem, że konwersja na float wprowadzi pewne niedokładności do wyników, ale wydaje się, że działa absolutnie dobrze.
Martin Smith

1
Jedną z wad tej metody jest to, że jeśli zaczniesz od wersji „2.0”, zmieni się ona na „2”. Prawdopodobnie jest to w porządku dla osoby zadającej pytanie, ale musiałem być w stanie zachować jedno zero po przecinku, nie zachowując żadnych innych zer końcowych. Odpowiedź @ user1959416 rozwiązuje ten problem.
Mason G. Zhwiti

6
Ogólnie rzecz biorąc, liczba zmiennoprzecinkowa jest bardzo złym wyborem do przechowywania liczb. Wystąpią błędy zaokrągleń, ponieważ nie jest to dokładny typ. Nigdy nie używaj pływaka.
HLGEM

Komentarz dotyczący formatowania
liczb zmiennoprzecinkowych

Czy mam rację sądząc, że skala i precyzja ułamka dziesiętnego może przekroczyć liczbę zmiennoprzecinkową i dlatego mogą wystąpić przypadki, w których (przy znaczącej liczbie ponad 17 cyfr) ta odpowiedź nie zadziała?
Caius Jard

31

Możesz użyć FORMAT()funkcji (SqlAzure i 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'

Zachowaj ostrożność podczas używania z FLOAT (lub REAL): nie używaj g17ani większego ( g8lub większego z REAL), ponieważ ograniczona precyzja reprezentacji maszyny powoduje niepożądane efekty:

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'

Ponadto należy pamiętać, że zgodnie z dokumentacją :

FORMAT opiera się na obecności środowiska uruchomieniowego języka wspólnego (CLR) .NET Framework. Ta funkcja nie będzie zdalna, ponieważ zależy od obecności CLR. Zdalne uruchomienie funkcji wymagającej środowiska CLR spowodowałoby błąd na serwerze zdalnym.

Działa również w SqlAzure.


Dla moich celów znalazłem ciąg formatu g8 sformatowany jako „1e-08”, co nie było tym, o co mi chodziło. Ta odpowiedź doprowadziła mnie do jednej, której mogłem użyć
Caius Jard

17
SELECT CONVERT(DOUBLE PRECISION, [ColumnName])

SQL Server 2008 i
nowsze

co się stanie, gdy numer będzie wyglądał jak „123,10705000000”? Próbowałem z SELECT CONVERT (DOUBLE PRECISION, 123.10705000000), ale w odpowiedzi otrzymałem „123.107”. i chcę na wyjściu „123.10705”? Czy jest jakiś sposób? Nie chcę używać CHARINDEX.
Bhavika Zimbar

16

Byłem niechętny rzucaniu na zmiennoprzecinkowe ze względu na możliwość umieszczenia większej liczby cyfr w moim ułamku dziesiętnym niż może to reprezentować liczba zmiennoprzecinkowa

FORMAT w przypadku użycia ze standardowym ciągiem formatu .net 'g8' zwróciło notację naukową w przypadku bardzo małych miejsc po przecinku (np. 1e-08), która również była nieodpowiednia

Użycie niestandardowego ciągu formatu ( https://docs.microsoft.com/en-us/dotnet/standard/base-types/custom-numeric-format-strings ) pozwoliło mi osiągnąć to, co chciałem:

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

Jeśli chcesz, aby twoja liczba miała co najmniej jedno końcowe zero, więc 2.0 nie zmieni się w 2, użyj ciągu formatującego, takiego jak 0.0#####

Punkt dziesiętny jest zlokalizowany, więc kultury używające przecinka jako separatora dziesiętnego napotkają wynik z przecinkiem, w którym. jest

Oczywiście jest to niepożądana praktyka polegająca na tym, że warstwa danych wykonuje formatowanie (ale w moim przypadku nie ma innej warstwy; użytkownik dosłownie uruchamia procedurę składowaną i umieszcza wynik w wiadomości e-mail: /)


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

wydruki:

2.55

2
Ta metoda jest fajna, ponieważ pozostawia końcowe zero włączone, jeśli jest tylko zero. Więc 2.5500 zwraca 2.55, a 2.000 zwraca 2.0 zamiast 2. Świetne, gdy formatujesz rozmiary silnika w pojeździe ...
Mason G. Zhwiti

4
@ MasonG.Zhwiti Mam wątpliwości, że to zadziała z niektórymi dziesiętnymi z większą liczbą cyfr po przecinku, na przykład 232.33220003200: -)
gotqn

@gotqn Dobra uwaga, to zdecydowanie nie działa. Jednak w naszym konkretnym przypadku użycia (formatowanie rozmiarów silnika w samochodach) działa idealnie. :)
Mason G. Zhwiti

jak powiedział @gotqn ... to błędne, gdy liczba jest długa
Ofear

To nie działa dokładnie dla liczb takich jak 0,56000. przyniesie 56. Zabawne
Gunjan Shakya


3

Spróbuj tego :

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

Daje 20,55


1
REPLACE (TRIM (REPLACE (20,00; „0”, „”)), „”, „0”) pozostawia na końcu. => „20.”
Keith Sirmons

Doskonale pasuje do mojego przypadku i wydaje się być najprostszym rozwiązaniem. Kciuk i głosuj w górę!
Oak_3260548

Świetne rozwiązanie bez konieczności konwersji na float! jeden drobny problem spotted 0.000stało .. tutaj jest poprawka SELECT REPLACE(RTRIM(REPLACE(20.5500, "0", " ")), " ", "0")polegająca na przycinaniu tylko końcowych zer
wilson

2

Miałem podobny problem, ale musiałem również usunąć kropkę dziesiętną tam, gdzie nie było miejsca po przecinku, oto moje rozwiązanie, które dzieli liczbę dziesiętną na jej składniki i opiera liczbę znaków pobieranych z ciągu dziesiętnego na długości składnik frakcji (bez użycia CASE). Żeby było jeszcze ciekawiej, moja liczba została zapisana jako liczba zmiennoprzecinkowa bez miejsc po przecinku.

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

Rezultat jest bolesny, wiem, ale dotarłem tam, z dużą pomocą powyższych odpowiedzi.


2

Najlepszym sposobem jest NIE konwertowanie na FLOAT lub MONEY przed konwersją ze względu na możliwość utraty precyzji. Tak więc bezpieczne sposoby mogą wyglądać następująco:

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

gdzie @ wartość może być dowolną liczbą dziesiętną (x, y)


@abatishchev, fn_ jest preferowanym prefiksem dla funkcji sql, prefiksy są używane w standardowej konwencji kodowania i nazewnictwa, a także w najlepszych praktykach, możemy dostosować nasze prefiksy, ale w przypadku najlepszych praktyk używamy sp_do procedury składowanej, fn_funkcji, tbltabel itp. na ... nie jest to wymagane, ale jest to najlepsze praktyki dotyczące porządkowania naszych baz danych.
japzdivino

@japongskie: przepraszam, ale nie. W ogóle nie są potrzebne przedrostki. To jest faktycznie najgorsza praktyka. Zobacz 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 i wiele więcej
abatishchev

@abatishchev, oh rozumiem .. więc nie będę już podążał za nauką w szkole .. haha ​​LOL, skoro twój link pochodzi z mdsn, dzięki za ten, zmienię teraz moją najlepszą praktykę .. :)
japzdivino

2

Miałem podobny problem, potrzebny do przycięcia końcowych zer z liczb takich jak xx0000,x00000,xxx000

Użyłem:

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

Kod to nazwa pola z numerem do przycięcia. Mam nadzieję, że to pomoże komuś innemu.


Ten komentarz działa dobrze, gdy używasz wersji SQL Server, która jest na tyle stara, że ​​nie możesz korzystać z funkcji TRIM lub FORMAT wbudowanych w SQL Server.
David Parvin

Znalazłem jeden problem z tą odpowiedzią. Jeśli wartość w polu „kod” jest podobna do „10”, zwróci „1”. Jeśli liczba to „10 .00”, myślę, że ignoruje również przecinek dziesiętny.
David Parvin

1

Inna opcja...

Nie wiem, jak wydajne jest to, ale wygląda na to, że działa i nie przechodzi przez float:

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

Środkowa linia usuwa końcowe spacje, dwie zewnętrzne usuwają kropkę, jeśli nie ma cyfr dziesiętnych


1

Musiałem usunąć końcowe zera z moich miejsc dziesiętnych, aby móc wyprowadzić ciąg o określonej długości z tylko początkiem zerami

(np. potrzebowałem wypisać 14 znaków, aby 142.023400 stało się 000000142.0234),

Kiedyś parsename, reversea cast as intusunięcie zer:

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

(Aby uzyskać moje wiodące zera, mógłbym powtórzyć poprawną liczbę zer w oparciu o długość powyższego i połączyć to na początku powyższego)

Mam nadzieję, że to komuś pomoże.


@Protiguous Ponieważ istnieje 1 punkt dziesiętny, wartość 2.5500 jest odczytywana jak <nazwa_schematu>. <nazwa_obiektu>. Drugi parametr o wartości 2 zwraca nazwę schematu, a drugi parametr o wartości 1 zwraca nazwę obiektu. Zwykle byłby używany jak PARSENAME ('dbo.TableName', 2), aby zwrócić dbo lub PARSENAME ('dbo.TableName', 1), aby zwrócić TableName.
Ali

Witam ... Widzę, że mój alias jest oznaczony w Twoim komentarzu. Nie wiem dlaczego?
Protiguous

Dokładne pytanie, które zadałeś mi 26 maja 2020 r., Brzmiało „Jak ma tu działać PARSENAME ???” Przepraszam, że tak długo trwało udzielanie odpowiedzi.
Ali

1

w TSQL można usunąć zera wiodące i końcowe

  1. Przekonwertuj go na ciąg przy użyciu funkcji STR TSQL, jeśli nie na łańcuch, Następnie

  2. Usuń zera wiodące i końcowe

    SELECT REPLACE(RTRIM(LTRIM(REPLACE(AccNo,'0',' '))),' ','0') AccNo FROM @BankAccount
    
  3. Więcej informacji na forum .


4
Trochę brzydka, ale ta wersja zabija resztki „.”:REPLACE(RTRIM(REPLACE(REPLACE(RTRIM(REPLACE(X,'0',' ')),' ','0'),'.',' ')),' ','.')
Chris B

1
Uważaj, jeśli liczba nie jest dziesiętna, spowoduje to również obcięcie zera. CHARINDEX ('.', @ Number)! = 1 przetestuje to.
Muflix

Moja poprzednia kontrola dziesiętna jest błędna. Tutaj jest lepiej: Wybierz Len (@Test) - Len (Zastąp (@Test, 'a', '')) jako
wyjaśnienie liczby znaków

0

Co powiesz na to? Zakładając, że dane trafiają do Twojej funkcji jako @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

Czy nie zastąpi to również zer (zer) między liczbami? Nie myśl, że wymiana działa tylko na końcach ...
Mtk

0

Rozumiem, że to stary post, ale chciałbym udostępnić SQL, który wymyśliłem

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

Spróbuj tego.

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

0

Najłatwiejszym sposobem jest RELACJA wartości jako FLOAT, a następnie do typu danych łańcuchowych.

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

-1

Spróbuj tego:

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

Wolę tutaj odpowiedź @ user1959416, ponieważ nie zmienia ona wartości. Na przykład, zaczynając od 2,5550, Twoja metoda zwraca 2,56, podczas gdy ich zwraca 2,555.
Mason G. Zhwiti

-1

Wiem, że ten wątek jest bardzo stary, ale dla tych, którzy nie używają programu SQL Server 2012 lub nowszego lub nie mogą używać funkcji FORMAT z jakiegokolwiek powodu, działa to następująco.

Ponadto wiele rozwiązań nie działało, jeśli liczba była mniejsza niż 1 (np. 0,01230000).

Należy pamiętać, że poniższe czynności nie działają z liczbami ujemnymi.

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

Zwraca odpowiednio 10,012345 i 0,012345.


-1

Spróbuj tego:

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

ten zwraca 999999, OP prosi o usunięcie końcowych zer.
japzdivino

-1

Kolumna DECIMAL (9,6) zostanie przekonwertowana na zmiennoprzecinkową bez utraty precyzji, więc CAST (... AS float) załatwi sprawę.


@HLGEM: mówienie, że zmiennoprzecinkowy jest złym wyborem do przechowywania liczb i „Nigdy nie używaj liczby zmiennoprzecinkowej” jest niepoprawne - wystarczy znać swoje liczby, np. Pomiary temperatury byłyby niezłe.

@abatishchev i @japongskie: przedrostki przed zapisanymi procedurami i funkcjami SQL są nadal dobrym pomysłem, jeśli nie są wymagane; wspomniane łącza instruują tylko, aby nie używać przedrostka „sp_” dla procedur składowanych, których nie należy używać, inne prefiksy są w porządku, np. „usp_” lub „spBob_”

Odniesienie: „Wszystkie liczby całkowite zawierające 6 lub mniej znaczących cyfr dziesiętnych można konwertować na wartości zmiennoprzecinkowe IEEE 754 bez utraty precyzji”: https://en.wikipedia.org/wiki/Single-precision_floating-point_format

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.