¿Cómo formateo un número con comas en T-SQL?

198

Estoy ejecutando algunas consultas administrativas y compilando resultados sp_spaceuseden SQL Server 2008 para ver las relaciones de espacio de datos / índices de algunas tablas en mi base de datos. Por supuesto, estoy obteniendo todo tipo de números grandes en los resultados y mis ojos están comenzando a brillar. Sería realmente conveniente si pudiera formatear todos esos números con comas (987654321 se convierte en 987,654,321). Es curioso que en todos los años que he usado SQL Server, este problema nunca ha surgido ya que la mayoría del tiempo estaría formateando en la capa de presentación, pero en este caso el resultado de T-SQL en SSMS es la presentación.

He considerado simplemente crear un CLR UDF simple para resolver esto, pero parece que esto debería ser posible en el viejo T-SQL. Entonces, plantearé la pregunta aquí: ¿cómo se hace el formato numérico en T-SQL de vainilla?

mattmc3
fuente
77
¿"Informes -> Uso de disco por tabla" hace lo que necesita de una manera estéticamente lo suficientemente agradable?
Martin Smith
1
@ Martin - ¡Realmente increíble! Ni siquiera sabía que existía. He llevado algunos de mis scripts de DBA conmigo durante casi una década, así que me los perdí por completo. Aún así, creo que esta pregunta es una parte importante de la base de conocimiento T-SQL en stackoverflow, pero para mi problema específico, esto es realmente útil.
mattmc3
8
Con SQL Server 2012 + puede usar la función FORMAT (). por ejemplo, '#, ##. 000' msdn.microsoft.com/en-us/library/hh213505.aspx
Volvox

Respuestas:

184

En SQL Server 2012 y versiones posteriores, esto formateará un número con comas:

select format([Number], 'N0')

También puede cambiar 0el número de decimales que desee.

Thomas Mueller
fuente
16
Esta es ahora la mejor respuesta desde la introducción de la formatfunción.
mattmc3
Vale la pena notar el tercer parámetro (opcional) culture.
Samuele Colombo
OP especificó SQL Server 2008
foremaro
254

Si bien estoy de acuerdo con todos, incluido el OP, que dice que el formateo se debe realizar en la capa de presentación, este formateo se puede lograr en T-SQL al moneyconvertirlo y luego convertirlo a varchar. Sin embargo, esto incluye decimales finales, que podrían ser eliminados SUBSTRING.

SELECT CONVERT(varchar, CAST(987654321 AS money), 1)
Phil Hunt
fuente
12
Si bien estoy de acuerdo en que el formateo en general debería ocurrir en otro lugar, todos damos por sentado las funciones de formateo de la fecha. La inserción de coma se puede hacer como se muestra aquí. +1.
EBarr
44
Sin embargo, esto no funciona para otros estilos de formato de mony. En Suiza escribimos Money, por ejemplo, de esta forma: 987'654'321.00 ¿Cómo hacer eso?
Daniel
66
Puede hacer un reemplazo SELECCIONAR REEMPLAZAR (CONVERTIR (varchar, CAST (987654321 COMO dinero), 1), ',', '' '')
Sudadera con capucha
44
Si bien estoy de acuerdo en que el formateo debe realizarse en la capa de presentación siempre que sea posible, ciertamente hay momentos, como con las alertas Ignite / DPA, de que el correo electrónico que recibo es la capa de presentación. La única forma de poner comas en un lugar como ese es a través de SQL. Tener comas en grandes números es extraordinariamente útil en esos casos.
PseudoToad
1
Todos quieren decirte qué "debería" hacerse, pero no es de eso de lo que se trata el diseño de tu propio código. Si todos solo hicieran lo que "debería" hacerse, entonces perderíamos ese espíritu de inventiva y la capacidad de hackear cosas para resolver un problema rápidamente con un mínimo esfuerzo y esfuerzo.
Geoff Griswald
59

Recomiendo Reemplazar en lugar de Substring para evitar problemas de longitud de cadena:

REPLACE(CONVERT(varchar(20), (CAST(SUM(table.value) AS money)), 1), '.00', '')
zomf
fuente
3
Aunque la conversión de dinero no debería cambiar nunca, me gusta la garantía de no salirse de los límites que Reemplazar ofrece sobre Substring.
Sean
48

Para las implementaciones de SQL Server 2012+, tendrá la capacidad de usar FORMAT para aplicar el formato de cadena a los tipos de datos sin cadena.

En la pregunta original, el usuario había solicitado la posibilidad de usar comas como miles de separadores. En una pregunta cerrada como duplicada , el usuario había preguntado cómo podían aplicar el formato de moneda. La siguiente consulta muestra cómo realizar ambas tareas. También demuestra la aplicación de la cultura para hacer de esta una solución más genérica (abordando la función de Tsiridis Dimitris para aplicar el formato especial griego)

-- 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 para lo anterior

billinkc
fuente
1
Gran parte, esto será útil :)
jediCouncilor
1
Fiddle está roto, ahora diceString index out of range: 33
Jeff Puckett
1
@JeffPuckettII Sí, es una pena que el violín para SQL Server ya no funcione. Afortunadamente, debería poder pegar lo anterior en cualquier herramienta de consulta que esté conectada a SQL Server 2012+
billinkc
20

Demo 1

Demuestra agregar comas:

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

Demo 2

Demuestra comas y puntos decimales. Observe que redondea el último dígito si es necesario.

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

Compatibilidad

SQL Server 2012+.

Aplazamiento de pago
fuente
2
¡Éste es el indicado! Funciona con len (columna) y solo con columna, mientras que otra solución de 2012+ que probé no funcionó.
Graham Laight
1
¡Excelente! Esta es la respuesta que estaba buscando (para usar con T-SQL, incluso en SEDE )
ashleedawg
10

Por favor, intente con la siguiente consulta:

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

Formato con punto decimal derecho:

SELECT FORMAT(987654321,'#,###,##0.###\,###')
Tiến Dũng
fuente
3
Sí, la forma correcta ahora que tenemos la FORMATfunción es SELECT format(123456789987654321,'###,##0'), o más simplemente, select format(123456789987654321, 'N0')como respondió @ThomasMueller.
mattmc3
FORMAT es una pesadilla de rendimiento: comienza a usarlo y depende de él, luego descubre que su base de datos no puede escalar. Y ahora está integrado en una docena de funciones y no puedes escapar de él. Nunca use FORMAT.
Pxtl
9
SELECT REPLACE(CONVERT(varchar(20), (CAST(9876543 AS money)), 1), '.00', '')

salida = 9,876,543

y puede reemplazar 9876543 por el nombre de su columna.

hojjat.mi
fuente
7

Intenté el truco de dinero anterior, y esto funciona muy bien para valores numéricos con dos o menos dígitos significativos. Creé mi propia función para formatear números con decimales:

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
fuente
4

Esto pertenece en un comentario a la respuesta de Phil Hunt , pero lamentablemente no tengo el representante.

Para quitar el ".00" del final de su cadena de números, el parsename es muy útil. Tokeniza cadenas delimitadas por puntos y devuelve el elemento especificado, comenzando con el token más a la derecha como elemento 1.

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

Rendimientos "987,654,321"

Apoxy
fuente
3

aquí hay otro t-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
Charles Bretana
fuente
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
fuente
1

Aquí hay una función escalar que estoy usando que corrige algunos errores en un ejemplo anterior (arriba) y también maneja valores decimales (al número especificado de dígitos) (EDITADO para que también funcione con 0 y números negativos). Otra nota, el método de conversión como dinero anterior se limita al tamaño del tipo de datos DINERO, y no funciona con decimales de 4 (o más) dígitos. Ese método es definitivamente más simple pero menos flexible.

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
usuario2230239
fuente
1

Otro UDF que, con suerte, es lo suficientemente genérico y no hace suposiciones sobre si desea redondear a un número específico de decimales:

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
fuente
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

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

/ * Esta función necesita 3 argumentos: el primer argumento es el @Numero_str que el número como entrada de datos, y los otros 2 argumentos especifican cómo se formateará la información para la salida, esos argumentos son @Pos_Enteros y @Pos_Decimales que especifican cuántos Enteros y decimales que desea mostrar para el número que pasa como argumento de entrada. * /

Francisco Cabrera
fuente
0

Para SQL Server antes de 2012 que no incluye la función FORMAT, cree esta función:

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 

seleccione dbo.FormatCurrency (12345678) devuelve $ 12,345,678.00

Suelta el $ si solo quieres comas.

StevenJe
fuente