Как мне отформатировать число с запятыми в T-SQL?

202

Я выполняю некоторые административные запросы и собираю результаты sp_spaceusedв SQL Server 2008, чтобы посмотреть на соотношение данных / индекса для некоторых таблиц в моей базе данных. Конечно, я получаю много разных результатов, и мои глаза начинают затуманиваться. Было бы очень удобно, если бы я мог отформатировать все эти числа запятыми (987654321 становится 987,654,321). Забавно, что за все те годы, когда я использовал SQL Server, эта проблема никогда не возникала, так как большую часть времени я занимался форматированием на уровне представления, но в этом случае результатом T-SQL в SSMS является представление.

Я решил просто создать простую ULF CLR, чтобы решить эту проблему, но кажется, что это должно быть выполнимо в простом старом T-SQL. Итак, я поставлю вопрос здесь - как вы делаете числовое форматирование в vanilla T-SQL?

mattmc3
источник
7
Делает ли «Отчеты -> Использование диска по таблице» то, что вам нужно, достаточно эстетически?
Мартин Смит
1
@Martin - Действительно круто! Даже не знал, что существует. Я носил с собой некоторые из моих сценариев DBA в течение почти десятилетия, поэтому я полностью пропустил это. Тем не менее, я думаю, что этот вопрос является важной частью базы знаний T-SQL по stackoverflow, но для моей конкретной проблемы это действительно удобно.
mattmc3
8
С SQL Server 2012 + вы можете использовать функцию FORMAT (). например, '#, ##. 000' msdn.microsoft.com/en-us/library/hh213505.aspx
Volvox

Ответы:

188

В SQL Server 2012 и выше, это будет форматировать число с запятыми:

select format([Number], 'N0')

Вы также можете изменить 0количество знаков после запятой, которое вы хотите.

Томас Мюллер
источник
16
Теперь это лучший ответ с момента введения formatфункции.
mattmc3
Стоит отметить третий (необязательный) параметр culture.
Самуэль Коломбо
ОП указал SQL Server 2008
foremaro
254

Хотя я согласен со всеми, включая OP, который говорит, что форматирование должно выполняться на уровне представления, это форматирование может быть выполнено в T-SQL путем moneyпреобразования и последующего преобразования в varchar. Это включает конечные десятичные дроби, которые могут быть зациклены с SUBSTRING.

SELECT CONVERT(varchar, CAST(987654321 AS money), 1)
Фил Хант
источник
12
Хотя я согласен с тем, что обычно форматирование должно происходить где-то еще, мы все принимаем функции форматирования даты как должное. Вставка запятой может быть выполнена, как показано здесь. +1.
EBarr
4
Однако это не работает для других стилей форматирования mony. В Швейцарии мы пишем деньги, например, в такой форме: 987'654'321.00 Как это сделать?
Даниэль
6
Вы могли бы сделать замену SELECT REPLACE (CONVERT (varchar, CAST (987654321 AS money), 1), ',', '' '')
Hoody
4
Хотя я согласен с тем, что форматирование следует выполнять на уровне представления, где это возможно, в определенные моменты, например, с оповещениями Ignite / DPA, полученное сообщение является уровнем представления. Единственный способ поместить запятые в такое место - через SQL. Наличие запятых в больших количествах чрезвычайно полезно в таких случаях.
PseudoToad
1
Каждый хочет сказать вам, что «должно» быть сделано, но это не то, для чего разрабатывается ваш собственный код. Если бы все делали только то, что «должно» быть сделано, мы теряем дух изобретательности и способность объединять усилия для быстрого решения проблемы с минимальными усилиями и усилиями.
Джефф Грисвальд
59

Я бы рекомендовал Replace вместо Substring, чтобы избежать проблем с длиной строки:

REPLACE(CONVERT(varchar(20), (CAST(SUM(table.value) AS money)), 1), '.00', '')
zomf
источник
3
Несмотря на то, что конверсия денег никогда не должна меняться, мне нравится гарантия не выходить за пределы, которые предлагает Replace вместо Substring.
Шон
48

Для реализаций SQL Server 2012+ у вас будет возможность использовать FORMAT для применения строкового форматирования к нестроковым типам данных.

В первоначальном вопросе пользователь запросил возможность использовать запятые в качестве разделителей тысяч. В закрытом и повторяющемся вопросе пользователь спросил, как можно применить форматирование валюты. Следующий запрос показывает, как выполнить обе задачи. Это также демонстрирует применение культуры, чтобы сделать это более универсальным решением (обращаясь к функции Циридиса Димитриса, чтобы применить греческое специальное форматирование)

-- 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 для вышеупомянутого

billinkc
источник
1
Большая доля, это пригодится :)
jediCouncilor
1
Скрипка сломана, теперь она говоритString index out of range: 33
Джефф Пукетт
1
@JeffPuckettII Да, жаль, что скрипка для SQL Server больше не работает. К счастью, вы сможете вставить вышеперечисленное в любой инструмент запросов, подключенный к SQL Server 2012+
billinkc
20

Демо 1

Демонстрирует добавление запятых:

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

Демо 2

Демонстрирует запятые и десятичные точки. Обратите внимание, что при необходимости округляется последняя цифра.

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

Совместимость

SQL Server 2012+,

Контанго
источник
2
Это тот самый! Работает как с len (column), так и с просто column - тогда как другое решение 2012+, которое я пробовал, не работает.
Грэм Лайт
1
Большой! Это ответ, который я искал (для использования с T-SQL, в том числе на SEDE )
ashleedawg
10

Пожалуйста, попробуйте следующий запрос:

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

Формат с правой десятичной точкой:

SELECT FORMAT(987654321,'#,###,##0.###\,###')
Tiến Dũng
источник
3
Да, теперь, когда у нас есть эта FORMATфункция , правильный путь SELECT format(123456789987654321,'###,##0'), или, проще говоря, select format(123456789987654321, 'N0')@ThomasMueller ответил.
mattmc3
FORMAT - это кошмар производительности - вы начинаете использовать его и зависите от него, а затем обнаруживаете, что ваша база данных не может масштабироваться. И теперь это встроено в дюжину функций, и вы не можете избежать этого. Никогда не используйте FORMAT.
Pxtl
9
SELECT REPLACE(CONVERT(varchar(20), (CAST(9876543 AS money)), 1), '.00', '')

выход = 9 876 543

и вы можете заменить 9876543 вашим именем столбца.

hojjat.mi
источник
7

Попробовал трюк с деньгами выше, и это прекрасно работает для числовых значений с двумя или менее значащими цифрами. Я создал свою собственную функцию для форматирования чисел с десятичными знаками:

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
источник
4

Это относится к комментарию к ответу Фила Ханта , но, увы, у меня нет представителя.

Чтобы удалить ".00" из конца вашей числовой строки, parsename очень удобен. Он токенизирует строки, разделенные точкой, и возвращает указанный элемент, начиная с самого правого токена как элемента 1.

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

Выход "987,654,321"

Apoxy
источник
3

вот еще один т-sql UDF

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
Чарльз Бретана
источник
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')`
Циридис Димитрис
источник
1

Вот скалярная функция, которую я использую, которая исправляет некоторые ошибки в предыдущем примере (выше), а также обрабатывает десятичные значения (с указанным числом цифр) (EDITED, чтобы также работать с 0 и отрицательными числами). Еще одно замечание: метод приведения в качестве денег выше ограничен размером типа данных MONEY и не работает с 4 (или более) десятичными цифрами. Этот метод определенно проще, но менее гибок.

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
user2230239
источник
1

Еще один UDF, который, как мы надеемся, достаточно универсален и не делает предположений о том, хотите ли вы округлить до определенного числа десятичных разрядов:

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
Митчелл Стайлз
источник
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

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

/ * Эта функция нуждается в 3 аргументах: первый аргумент - это @Numero_str, который Number в качестве ввода данных, а два других аргумента указывают, как информация будет отформатирована для вывода, это аргументы @Pos_Enteros и @Pos_Decimales, которые указывают, сколько Целые и десятичные разряды, которые вы хотите показать для числа, которое вы передаете в качестве входного аргумента. * /

Франсиско Кабрера
источник
0

Для SQL Server до 2012 года, который не включает функцию FORMAT, создайте эту функцию:

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 

выберите dbo.FormatCurrency (12345678) возвращает $ 12 345 678,00

Бросьте $, если вы просто хотите запятые.

StevenJe
источник