Jak sformatować liczbę za pomocą przecinków w T-SQL?

201

Korzystam z niektórych zapytań administracyjnych i kompiluję wyniki z sp_spaceusedSQL Server 2008, aby sprawdzić proporcje przestrzeni danych / indeksu niektórych tabel w mojej bazie danych. Oczywiście w wynikach pojawiają się różnego rodzaju duże liczby, a moje oczy zaczynają się połyskiwać. Byłoby naprawdę wygodne, gdybym mógł sformatować wszystkie te liczby przecinkami (987654321 staje się 987,654,321). Zabawne, że przez wszystkie lata korzystania z programu SQL Server ten problem nigdy nie pojawiał się, ponieważ przez większość czasu zajmowałem się formatowaniem w warstwie prezentacji, ale w tym przypadku wynikiem T-SQL w SSMS jest prezentacja.

Zastanawiałem się nad stworzeniem prostego CLR UDF, aby to rozwiązać, ale wydaje się, że powinno to być możliwe w zwykłym starym T-SQL. Zadam więc pytanie - jak przeprowadzacie formatowanie numeryczne w waniliowym T-SQL?

mattmc3
źródło
7
Czy „Raporty -> Wykorzystanie dysku według tabeli” robi to, czego potrzebujesz w wystarczająco estetyczny sposób?
Martin Smith,
1
@Martin - Naprawdę niesamowite! Nawet nie wiedziałem, że istnieje. Przez prawie dekadę nosiłem ze sobą niektóre skrypty DBA, więc całkowicie tego nie zauważyłem. Myślę jednak, że to pytanie jest ważną częścią bazy wiedzy T-SQL na temat przepełnienia stosu, ale w przypadku mojego konkretnego problemu jest to bardzo przydatne.
mattmc3,
8
W SQL Server 2012 + można użyć funkcji FORMAT (). np. „#, ##. 000” msdn.microsoft.com/en-us/library/hh213505.aspx
Volvox

Odpowiedzi:

187

W SQL Server 2012 i nowszych wersjach formatuje liczbę przecinkami:

select format([Number], 'N0')

Możesz także zmienić 0liczbę miejsc dziesiętnych, które chcesz.

Thomas Mueller
źródło
16
To jest teraz najlepsza odpowiedź od czasu wprowadzenia formatfunkcji.
mattmc3
warto zauważyć trzeci (opcjonalny) parametr culture.
Samuele Colombo
OP określił SQL Server 2008
foremaro
254

Chociaż zgadzam się ze wszystkimi, w tym z OP, który mówi, że formatowanie powinno odbywać się w warstwie prezentacji, formatowanie to można wykonać w języku T-SQL poprzez rzutowanie na, moneya następnie konwersję na varchar. Obejmuje to jednak dziesiętne, które można zapętlić SUBSTRING.

SELECT CONVERT(varchar, CAST(987654321 AS money), 1)
Phil Hunt
źródło
12
Chociaż zgadzam się, że generalnie formatowanie powinno odbywać się gdzie indziej, wszyscy uznajemy funkcje formatowania za coś oczywistego. Wstawianie przecinków można wykonać, jak pokazano tutaj. +1.
EBarr,
4
Nie działa to jednak w przypadku innych stylów mony. W Szwajcarii piszemy Pieniądze na przykład w tej formie: 987'654'321.00 Jak to zrobić?
Daniel,
6
Możesz zrobić wymianę WYBIERZ WYMIANĘ (CONVERT (varchar, CAST (987654321 AS money), 1), ',', '' '')
Hoody
4
Chociaż zgadzam się, że formatowanie powinno odbywać się w warstwie prezentacji tam, gdzie to możliwe, z pewnością są chwile, takie jak w przypadku alertów Ignite / DPA, że e-mail, który otrzymuję, jest warstwą prezentacji. Jedynym sposobem umieszczenia przecinków w takim miejscu jest SQL. Duże liczby przecinków są w takich przypadkach wyjątkowo pomocne.
PseudoToad,
1
Każdy chce ci powiedzieć, co należy zrobić, ale nie o to chodzi w projektowaniu własnego kodu. Gdyby wszyscy zrobili tylko to, co „należy” zrobić, tracimy ducha pomysłowości i umiejętności wspólnego hakowania, aby szybko rozwiązać problem przy minimalnym zamieszaniu i wysiłku.
Geoff Griswald
59

Polecam Zastąp zamiast podciągów, aby uniknąć problemów z długością łańcucha:

REPLACE(CONVERT(varchar(20), (CAST(SUM(table.value) AS money)), 1), '.00', '')
zomf
źródło
3
Mimo że konwersja pieniędzy nie powinna się nigdy zmienić, podoba mi się gwarancja, że ​​nie wyjdzie poza granice, które oferuje funkcja Zastąp w stosunku do podciągów.
Sean
48

W przypadku implementacji programu SQL Server 2012+ będziesz mieć możliwość użycia FORMATU do zastosowania formatowania ciągów do typów danych nieciągłych.

W pierwotnym pytaniu użytkownik poprosił o możliwość używania przecinków jako tysięcy separatorów. W zamkniętym zduplikowanym pytaniu użytkownik zapytał, jak zastosować formatowanie walut. Poniższe zapytanie pokazuje, jak wykonać oba zadania. Pokazuje także zastosowanie kultury, aby uczynić to bardziej ogólnym rozwiązaniem (odnosząc się do funkcji Tsiridis Dimitris do zastosowania specjalnego greckiego formatowania)

-- FORMAT
-- http://msdn.microsoft.com/en-us/library/hh213505(v=sql.110).aspx
-- FORMAT does not do conversion, that's the domain of cast/convert/parse etc
-- Only accepts numeric and date/time data types for formatting. 
--
-- Formatting Types
-- http://msdn.microsoft.com/en-us/library/26etazsy.aspx

-- Standard numeric format strings
-- http://msdn.microsoft.com/en-us/library/dwhawy9k.aspx
SELECT
    -- c => currency
    -- n => numeric
    FORMAT(987654321, N'N', C.culture) AS some_number
,   FORMAT(987654321, N'c', C.culture) AS some_currency
,   C.culture
FROM
    (
        -- Language culture names
        -- http://msdn.microsoft.com/en-us/library/ee825488(v=cs.20).aspx
        VALUES
            ('en-US')
        ,   ('en-GB')
        ,   ('ja-JP')
        ,   ('Ro-RO')
        ,   ('el-GR')
    ) C (culture);

SQLFiddle dla powyższych

billinkc
źródło
1
Świetny udział, to się przyda :)
radny jediC
1
Fiddle jest zepsuty, mówi terazString index out of range: 33
Jeff Puckett
1
@JeffPuckettII Tak, szkoda, że ​​skrzypce dla SQL Servera już nie działają. Na szczęście powinieneś być w stanie wkleić powyższe do dowolnego narzędzia do zapytań, które jest podłączone do SQL Server 2012+
billinkc
20

Demo 1

Pokazuje dodanie przecinków:

PRINT FORMATMESSAGE('The number is: %s', format(5000000, '#,##0'))
-- Output
The number is: 5,000,000

Demo 2

Pokazuje przecinki i przecinki dziesiętne. Zauważ, że w razie potrzeby zaokrągla ostatnią cyfrę.

PRINT FORMATMESSAGE('The number is: %s', format(5000000.759145678, '#,##0.00'))
-- Output
The number is: 5,000,000.76

Zgodność

SQL Server 2012+.

Contango
źródło
2
To jest jeden! Działa zarówno z len (kolumna), jak i tylko kolumna - podczas gdy inne rozwiązanie z 2012 roku, którego próbowałem, nie zadziałało.
Graham Laight,
1
Wspaniały! Oto odpowiedź, której szukałem (do użycia z T-SQL, w tym na SEDE )
ashleedawg
10

Spróbuj z poniższym zapytaniem:

SELECT FORMAT(987654321,'#,###,##0')

Formatuj z prawym przecinkiem dziesiętnym:

SELECT FORMAT(987654321,'#,###,##0.###\,###')
Tiến Dũng
źródło
3
Tak, właściwą drogą teraz, kiedy mamy tę FORMATfunkcję, jest SELECT format(123456789987654321,'###,##0'), lub prościej, select format(123456789987654321, 'N0')jak odpowiedział @ThomasMueller.
mattmc3
FORMAT to koszmar wydajności - zaczynasz go używać i polegasz na nim, a potem okazuje się, że Twoja baza danych nie może się skalować. A teraz jest wbudowany w kilkanaście funkcji i nie można od niego uciec. Nigdy nie używaj FORMATU.
Pxtl,
9
SELECT REPLACE(CONVERT(varchar(20), (CAST(9876543 AS money)), 1), '.00', '')

wyjście = 9 876 543

i możesz zastąpić 9876543 nazwą kolumny.

hojjat.mi
źródło
7

Wypróbowałem powyższą sztuczkę pieniężną, a to świetnie sprawdza się w przypadku wartości liczbowych zawierających dwie lub mniej znaczące cyfry. Stworzyłem własną funkcję do formatowania liczb dziesiętnych:

CREATE FUNCTION [dbo].[fn_FormatWithCommas] 
(
    -- Add the parameters for the function here
    @value varchar(50)
)
RETURNS varchar(50)
AS
BEGIN
    -- Declare the return variable here
    DECLARE @WholeNumber varchar(50) = NULL, @Decimal varchar(10) = '', @CharIndex int = charindex('.', @value)

    IF (@CharIndex > 0)
        SELECT @WholeNumber = SUBSTRING(@value, 1, @CharIndex-1), @Decimal = SUBSTRING(@value, @CharIndex, LEN(@value))
    ELSE
        SET @WholeNumber = @value

    IF(LEN(@WholeNumber) > 3)
        SET @WholeNumber = dbo.fn_FormatWithCommas(SUBSTRING(@WholeNumber, 1, LEN(@WholeNumber)-3)) + ',' + RIGHT(@WholeNumber, 3)



    -- Return the result of the function
    RETURN @WholeNumber + @Decimal

END
havana59er
źródło
4

To należy do komentarza do odpowiedzi Phila Hunta , ale niestety nie mam przedstawiciela.

Aby usunąć „.00” z końca łańcucha liczb, nazwa parsen jest bardzo przydatna. Tokenizuje łańcuchy rozdzielane kropką i zwraca określony element, zaczynając od tokena znajdującego się najbardziej na prawo jako element 1.

SELECT PARSENAME(CONVERT(varchar, CAST(987654321 AS money), 1), 2)

Daje „987,654,321”

Apoxy
źródło
3

oto kolejny UDF t-sql

CREATE FUNCTION dbo.Format(@num int)
returns varChar(30)
As
Begin
Declare @out varChar(30) = ''

  while @num > 0 Begin
      Set @out = str(@num % 1000, 3, 0) + Coalesce(','+@out, '')
      Set @num = @num / 1000
  End
  Return @out
End
Charles Bretana
źródło
2
`/* Author: Tsiridis Dimitris */
/* Greek amount format. For the other change the change on replace of '.' & ',' */
CREATE FUNCTION dbo.formatAmount  (
@amtIn as varchar(20)
) RETURNS varchar(20)
AS
BEGIN 

return cast(REPLACE(SUBSTRING(CONVERT(varchar(20), CAST(@amtIn AS money), 1),1,
LEN(CONVERT(varchar(20), CAST(@amtIn AS money), 1))-3), ',','.')
 + replace(RIGHT(CONVERT(varchar(20), CAST(@amtIn AS money), 1),3), '.',',') AS VARCHAR(20))

END

SELECT [geniki].[dbo].[formatAmount]('9888777666555.44')`
Tsiridis Dimitris
źródło
1

Oto używana przeze mnie funkcja skalarna, która naprawia niektóre błędy w poprzednim przykładzie (powyżej), a także obsługuje wartości dziesiętne (do określonej liczby cyfr) (EDYCJA działa również z 0 i liczbami ujemnymi). Inna uwaga, powyższa metoda przesyłania jako pieniądza jest ograniczona do rozmiaru typu danych PIENIĄDZE i nie działa z dziesiętnymi 4 (lub więcej) cyframi. Ta metoda jest zdecydowanie prostsza, ale mniej elastyczna.

CREATE FUNCTION [dbo].[fnNumericWithCommas](@num decimal(38, 18), @decimals int = 4) RETURNS varchar(44) AS
BEGIN
    DECLARE @ret varchar(44)

    DECLARE @negative bit; SET @negative = CASE WHEN @num < 0 THEN 1 ELSE 0 END

    SET @num = abs(round(@num, @decimals)) -- round the value to the number of decimals desired
    DECLARE @decValue varchar(18); SET @decValue = substring(ltrim(@num - round(@num, 0, 1)) + '000000000000000000', 3, @decimals)
    SET @num = round(@num, 0, 1) -- truncate the incoming number of any decimals
    WHILE @num > 0 BEGIN
        SET @ret = str(@num % 1000, 3, 0) + isnull(','+@ret, '')
        SET @num = round(@num / 1000, 0, 1)
    END
    SET @ret = isnull(replace(ltrim(@ret), ' ', '0'), '0') + '.' + @decValue
    IF (@negative = 1) SET @ret = '-' + @ret

    RETURN @ret
END

GO
użytkownik2230239
źródło
1

Kolejny UDF, który, mam nadzieję, jest dość ogólny i nie przyjmuje założeń, czy chcesz zaokrąglić do określonej liczby miejsc po przecinku:

CREATE FUNCTION [dbo].[fn_FormatNumber] (@number decimal(38,18))

RETURNS varchar(50)

BEGIN
    -- remove minus sign before applying thousands seperator
    DECLARE @negative bit
    SET @negative = CASE WHEN @number < 0 THEN 1 ELSE 0 END
    SET @number = ABS(@number)

    -- add thousands seperator for every 3 digits to the left of the decimal place
    DECLARE @pos int, @result varchar(50) = CAST(@number AS varchar(50))
    SELECT @pos = CHARINDEX('.', @result)
    WHILE @pos > 4
    BEGIN
        SET @result = STUFF(@result, @pos-3, 0, ',')
        SELECT @pos = CHARINDEX(',', @result)
    END

    -- remove trailing zeros
    WHILE RIGHT(@result, 1) = '0'
        SET @result = LEFT(@result, LEN(@result)-1)
    -- remove decimal place if not required
    IF RIGHT(@result, 1) = '.'
        SET @result = LEFT(@result, LEN(@result)-1)

    IF @negative = 1
        SET @result = '-' + @result

    RETURN @result
END
Mitchell Stiles
źródło
0
/*
  #------------------------------------------------------------------------#
  #            SQL Query Script                                            #
  #            ----------------                                            #
  # Funcion.:  dbo.fn_nDerecha ( Numero, Pos_Enteros, Pos_Decimales )      #
  #    Numero        : es el Numero o Valor a formatear                    #
  #    Pos_Enteros   : es la cantidad posiciones para Enteros              #
  #    Pos_Decimales : es la cantidad posiciones para Decimales            #
  #                                                                        #
  # OBJETIVO:  Formatear los Numeros con Coma y Justificado a la Derecha   #
  #  Por Ejemplo:                                                          #
  #   dbo.fn_nDerecha ( Numero, 9, 2 )         Resultado = ---,---,--9.99  #
  #               dado  Numero = 1234.56       Resultado =       1,234.56  #
  #               dado  Numero = -1.56         Resultado =          -1.56  #
  #               dado  Numero = -53783423.56  Resultado = -53,783,423.56  #
  #                                                                        #
  # Autor...:  Francisco Eugenio Cabrera Perez                             #
  # Fecha...:  Noviembre 25, 2015                                          #
  # Pais....:  Republica Dominicana                                        #
  #------------------------------------------------------------------------#
*/



CREATE FUNCTION [dbo].[fn_nDerecha]
(
    -- Agregue Argumentos, para personalizar la funcion a su conveniencia
    @Numero_str    varchar(max)
   ,@Pos_Enteros   int
   ,@Pos_Decimales int
)
RETURNS varchar(max)
AS
BEGIN
  --  Declare la variable del RETURN aqui, en este caso es RESULT
  declare @RESULTADO varchar(max)
  set     @RESULTADO = '****'

  -----------------------------------------------  --
  declare @Numero_num numeric(28,12)
  set     @Numero_num =
  (
  case when isnumeric(@Numero_str) = 0 
       then 0
       else round (convert( numeric(28,12), @Numero_str), @Pos_Decimales)
  end
  )
  --  -----------------------------------------------  --
  --  Aumenta @Pos_Enteros de @RESULTADO,
  --      si las posiciones de Enteros del dato @Numero_str es Mayor...
  --
  declare   @Num_Pos_Ent int
  set       @Num_Pos_Ent = len ( convert( varchar, convert(int, abs(@Numero_num) ) ) )
  --
  declare   @Pos_Ent_Mas int
  set       @Pos_Ent_Mas =
  (
  case when @Num_Pos_Ent > @Pos_Enteros
       then @Num_Pos_Ent - @Pos_Enteros
       else 0
  end
  )
  set       @Pos_Enteros = @Pos_Enteros + @Pos_Ent_Mas
  --
  --  -----------------------------------------------  --
  declare @p_Signo_ctd       int
  set     @p_Signo_ctd       = (case when @Numero_num < 1 then 1 else 0 end)
  --
  declare @p_Comas_ctd       int
  set     @p_Comas_ctd       = ( @Pos_Enteros - 1 ) / 3
  --
  declare @p_Punto_ctd       int
  set     @p_Punto_ctd       = (case when @Pos_Decimales > 0 then 1 else 0 end)
  --
  declare @p_input_Longitud  int
  set     @p_input_Longitud  = ( @p_Signo_ctd + @Pos_Enteros ) +
                                 @p_Punto_ctd + @Pos_Decimales
  --
  declare @p_output_Longitud int
  set     @p_output_Longitud = ( @p_Signo_ctd + @Pos_Enteros   + @p_Comas_ctd )
                             + ( @p_Punto_ctd + @Pos_Decimales )
  --
  --  ===================================================================  --


  declare @Valor_str varchar(max)
  set     @Valor_str = str(@Numero_num, @p_input_Longitud, @Pos_Decimales)

  declare @V_Ent_str varchar(max)
  set     @V_Ent_str = 
  (case when @Pos_Decimales > 0 
        then substring( @Valor_str, 0, charindex('.', @Valor_str, 0) )
        else            @Valor_str end)
  --
  declare @V_Dec_str varchar(max)
  set     @V_Dec_str = 
  (case when @Pos_Decimales > 0 
        then '.' + right(@Valor_str, @Pos_Decimales)
        else '' end)
  --
  set @V_Ent_str = convert(VARCHAR, convert(money, @V_Ent_str), 1) 
  set @V_Ent_str = substring( @V_Ent_str, 0, charindex('.', @V_Ent_str, 0) )
  --


  set @RESULTADO    = @V_Ent_str + @V_Dec_str 
  --
  set @RESULTADO = ( replicate( ' ', @p_output_Longitud - len(@RESULTADO) ) + @RESULTADO )
  --

  --  ===================================================================  -

- ================================================ =================== -

  RETURN @RESULTADO
END

  --  ===================================================================  --

/ * Ta funkcja wymaga 3 argumentów: pierwszy argument to @Numero_str, który Liczba jako dane wejściowe, a pozostałe 2 argumenty określają sposób formatowania informacji na wyjściu, argumenty te to @Pos_Enteros i @Pos_Decimales, które określają liczbę Miejsca całkowite i dziesiętne, które chcesz pokazać dla liczby, którą podajesz jako argument wejściowy. * /

Francisco Cabrera
źródło
0

W przypadku programu SQL Server przed 2012 r., Który nie zawiera funkcji FORMAT, utwórz tę funkcję:

CREATE FUNCTION FormatCurrency(@value numeric(30,2))
    RETURNS varchar(50)
    AS
    BEGIN
        DECLARE @NumAsChar VARCHAR(50)
        SET @NumAsChar = '$' + CONVERT(varchar(50), CAST(@Value AS money),1)
        RETURN @NumAsChar
    END 

wybierz dbo.FormatCurrency (12345678) zwraca 12 346 678,00 $

Upuść $, jeśli chcesz przecinki.

StevenJe
źródło