String.Format como funcionalidad en T-SQL?

90

Estoy buscando una función incorporada / función extendida en T-SQL para la manipulación de cadenas similar al String.Formatmétodo en .NET.

Jeff
fuente

Respuestas:

71

Si está utilizando SQL Server 2012 y superior, puede utilizar FORMATMESSAGE. p.ej.

DECLARE @s NVARCHAR(50) = 'World';
DECLARE @d INT = 123;
SELECT FORMATMESSAGE('Hello %s, %d', @s, @d)
-- RETURNS 'Hello World, 123'

Más ejemplos de MSDN: FORMATMESSAGE

SELECT FORMATMESSAGE('Signed int %i, %d %i, %d, %+i, %+d, %+i, %+d', 5, -5, 50, -50, -11, -11, 11, 11);
SELECT FORMATMESSAGE('Signed int with leading zero %020i', 5);
SELECT FORMATMESSAGE('Signed int with leading zero 0 %020i', -55);
SELECT FORMATMESSAGE('Unsigned int %u, %u', 50, -50);
SELECT FORMATMESSAGE('Unsigned octal %o, %o', 50, -50);
SELECT FORMATMESSAGE('Unsigned hexadecimal %x, %X, %X, %X, %x', 11, 11, -11, 50, -50);
SELECT FORMATMESSAGE('Unsigned octal with prefix: %#o, %#o', 50, -50);
SELECT FORMATMESSAGE('Unsigned hexadecimal with prefix: %#x, %#X, %#X, %X, %x', 11, 11, -11, 50, -50);
SELECT FORMATMESSAGE('Hello %s!', 'TEST');
SELECT FORMATMESSAGE('Hello %20s!', 'TEST');
SELECT FORMATMESSAGE('Hello %-20s!', 'TEST');
SELECT FORMATMESSAGE('Hello %20s!', 'TEST');

NOTAS:

  • Indocumentado en 2012
  • Limitado a 2044 caracteres
  • Para escapar del signo%, debe duplicarlo.
  • Si está registrando errores en eventos extendidos, la llamada FORMATMESSAGEaparece como un error (inofensivo)
g2server
fuente
1
Si está utilizando SQL 2012, puede usar la función FORMAT sin todas las complicaciones anteriores :)
Ingeniero
1
¡Esto debería tener muchos más votos! El hallazgo impresionante siempre se evita porque se supone que solo funcionaría con archivos msg_number.
Lankymart
1
@Lankymart, golpe! Estoy de acuerdo en que esta debería ser la respuesta aceptada: simple, incorporada y trabajada espléndidamente.
Robert Synoradzki
5
@bijayk solo acepta nombres de marcadores de posición específicos, por ejemplo,% s para cadenas,% i para ints.
g2server
2
@lostmylogin No hay ninguna string.Formatfuncionalidad de estilo en T-SQL, esto es lo más cercano que obtendrá.
Ian Kemp
53

eche un vistazo a xp_sprintf . ejemplo a continuación.

DECLARE @ret_string varchar (255)
EXEC xp_sprintf @ret_string OUTPUT, 
    'INSERT INTO %s VALUES (%s, %s)', 'table1', '1', '2'
PRINT @ret_string

El resultado se ve así:

INSERT INTO table1 VALUES (1, 2)

Acabo de encontrar un problema con el tamaño máximo (límite de 255 caracteres) de la cadena con esto, por lo que hay una función alternativa que puede usar:

create function dbo.fnSprintf (@s varchar(MAX), 
                @params varchar(MAX), @separator char(1) = ',')
returns varchar(MAX)
as
begin
declare @p varchar(MAX)
declare @paramlen int

set @params = @params + @separator
set @paramlen = len(@params)
while not @params = ''
begin
    set @p = left(@params+@separator, charindex(@separator, @params)-1)
    set @s = STUFF(@s, charindex('%s', @s), 2, @p)
    set @params = substring(@params, len(@p)+2, @paramlen)
end
return @s
end

Para obtener el mismo resultado que el anterior, llame a la función de la siguiente manera:

print dbo.fnSprintf('INSERT INTO %s VALUES (%s, %s)', 'table1,1,2', default)
Josh
fuente
7
Solo un FYI si alguno de sus parámetros contiene una coma, entonces no tiene suerte. Si pasa uno por accidente, tendrá algunos problemas para descubrir qué salió mal.
Kyle
15

He creado una función definida por el usuario para imitar la funcionalidad string.format. Puedes usarlo.

stringformat-in-sql

Karthik DV
fuente
Me gusta esta solución porque tengo reservas en contra del uso de funciones xp_ / SP en producción. Usé su código como base y permití el paso del delimitador, lo que elimina cualquier preocupación sobre el uso de comas en los datos.
Tim Friesen
4

Hay una forma, pero tiene sus limitaciones. Puede utilizar la FORMATMESSAGE()función. Le permite formatear una cadena usando un formato similar a la printf()función en C.

Sin embargo, la mayor limitación es que solo funcionará con mensajes en la tabla sys.messages. Aquí hay un artículo al respecto: microsoft_library_ms186788

Es una pena que no haya una manera más fácil de hacer esto, porque hay ocasiones en las que desea formatear una cadena / varchar en la base de datos. Con suerte, solo está buscando formatear una cadena de una manera estándar y puede usar la sys.messagestabla.

Casualmente, también podría usar la RAISERROR()función con una gravedad muy baja, la documentación de raiseerror incluso menciona hacer esto, pero los resultados solo se imprimen. Por lo tanto, no podría hacer nada con el valor resultante (por lo que entiendo).

¡Buena suerte!

jj.
fuente
1
Comprenda que esta es una respuesta antigua, pero la suposición FORMATMESSAGE()es incorrecta, sin embargo es comprensible porque no está documentada pero aceptará cualquier cadena como primer parámetro, vea esta respuesta de @ g2server .
Lankymart
@Lankymart Tienes razón, esta es una respuesta antigua. La capacidad de aceptar una cadena no se agregó hasta SQL 2012.
jj.
3

El t-sql sin formato está limitado a CHARINDEX (), PATINDEX (), REPLACE () y SUBSTRING () para la manipulación de cadenas. Pero con sql server 2005 y versiones posteriores, puede configurar funciones definidas por el usuario que se ejecutan en .Net, lo que significa que configurar una UDF string.format () no debería ser demasiado difícil.

Joel Coehoorn
fuente
2

Una idea más.

Aunque esta no es una solución universal, es simple y funciona, al menos para mí :)

Para un marcador de posición {0}:

create function dbo.Format1
(
    @String  nvarchar(4000),
    @Param0  sql_variant
)
returns nvarchar(4000)
as
begin
    declare @Null nvarchar(4) = N'NULL';

    return replace(@String, N'{0}', cast(isnull(@Param0, @Null) as nvarchar(4000)));    
end

Para dos marcadores de posición {0} y {1}:

create function dbo.Format2
(
    @String  nvarchar(4000),
    @Param0  sql_variant,
    @Param1  sql_variant
)
returns nvarchar(4000)
as
begin
    declare @Null nvarchar(4) = N'NULL';

    set @String = replace(@String, N'{0}', cast(isnull(@Param0, @Null) as nvarchar(4000)));
       return     replace(@String, N'{1}', cast(isnull(@Param1, @Null) as nvarchar(4000))); 
end

Para tres marcadores de posición {0}, {1} y {2}:

create function dbo.Format3
(
    @String  nvarchar(4000),
    @Param0  sql_variant,
    @Param1  sql_variant,
    @Param2  sql_variant
)
returns nvarchar(4000)
as
begin
    declare @Null nvarchar(4) = N'NULL';

    set @String = replace(@String, N'{0}', cast(isnull(@Param0, @Null) as nvarchar(4000)));
    set @String = replace(@String, N'{1}', cast(isnull(@Param1, @Null) as nvarchar(4000))); 
       return     replace(@String, N'{2}', cast(isnull(@Param2, @Null) as nvarchar(4000)));
end

y así...

Este enfoque nos permite usar estas funciones en la instrucción SELECT y con parámetros de tipos de datos nvarchar, number, bit y datetime.

Por ejemplo:

declare @Param0 nvarchar(10) = N'IPSUM' ,
        @Param1 int          = 1234567  ,
        @Param2 datetime2(0) = getdate();

select dbo.Format3(N'Lorem {0} dolor, {1} elit at {2}', @Param0, @Param1, @Param2);  
Vadim Loboda
fuente
1

Creo que hay una pequeña corrección al calcular la posición final.

Aquí está la función correcta

**>>**IF OBJECT_ID( N'[dbo].[FormatString]', 'FN' ) IS NOT NULL
DROP FUNCTION [dbo].[FormatString]
GO
/***************************************************
Object Name : FormatString
Purpose : Returns the formatted string.
Original Author : Karthik D V http://stringformat-in-sql.blogspot.com/
Sample Call:
SELECT dbo.FormatString ( N'Format {0} {1} {2} {0}', N'1,2,3' )
*******************************************/
CREATE FUNCTION [dbo].[FormatString](
    @Format NVARCHAR(4000) ,
    @Parameters NVARCHAR(4000)
)
RETURNS NVARCHAR(4000)
AS
BEGIN
    --DECLARE @Format NVARCHAR(4000), @Parameters NVARCHAR(4000) select @format='{0}{1}', @Parameters='hello,world'
    DECLARE @Message NVARCHAR(400), @Delimiter CHAR(1)
    DECLARE @ParamTable TABLE ( ID INT IDENTITY(0,1), Parameter VARCHAR(1000) )
    Declare @startPos int, @endPos int
    SELECT @Message = @Format, @Delimiter = ','**>>**

    --handle first parameter
     set @endPos=CHARINDEX(@Delimiter,@Parameters)
    if (@endPos=0 and @Parameters is not null) --there is only one parameter
        insert into @ParamTable (Parameter) values(@Parameters)
    else begin
        insert into @ParamTable (Parameter) select substring(@Parameters,0,@endPos)
    end

    while @endPos>0
    Begin
        --insert a row for each parameter in the 
        set @startPos = @endPos + LEN(@Delimiter)
        set @endPos = CHARINDEX(@Delimiter,@Parameters, @startPos)
        if (@endPos>0)
            insert into @ParamTable (Parameter) 
                select substring(@Parameters,@startPos,@endPos - @startPos)
            else
                insert into @ParamTable (Parameter) 
                select substring(@Parameters,@startPos,4000)            
    End

    UPDATE @ParamTable SET @Message = 
        REPLACE ( @Message, '{'+CONVERT(VARCHAR,ID) + '}', Parameter )
    RETURN @Message
END
Go
grant execute,references on dbo.formatString to public 
SP007
fuente
1

Aquí está mi versión. Puede ampliarse para adaptarse a más parámetros y puede ampliar el formato según el tipo. Actualmente, solo se formatean los tipos de fecha y fecha y hora.

Ejemplo:

select dbo.FormatString('some string %s some int %s date %s','"abcd"',100,cast(getdate() as date),DEFAULT,DEFAULT)
select dbo.FormatString('some string %s some int %s date time %s','"abcd"',100,getdate(),DEFAULT,DEFAULT)

Salida:

some string "abcd" some int 100 date 29-Apr-2017
some string "abcd" some int 100 date time 29-Apr-2017 19:40

Funciones:

create function dbo.FormatValue(@param sql_variant)
returns nvarchar(100)
begin
/*
Tejasvi Hegde, 29-April-2017
Can extend formatting here.
*/
    declare @result nvarchar(100)

    if (SQL_VARIANT_PROPERTY(@param,'BaseType') in ('date'))
    begin
       select @result = REPLACE(CONVERT(CHAR(11), @param, 106), ' ', '-')
    end
    else  if (SQL_VARIANT_PROPERTY(@param,'BaseType') in ('datetime','datetime2'))
    begin
       select @result = REPLACE(CONVERT(CHAR(11), @param, 106), ' ', '-')+' '+CONVERT(VARCHAR(5),@param,108)
    end
    else
    begin
       select @result = cast(@param as nvarchar(100))
    end
    return @result

/*
BaseType:
bigint
binary
char
date
datetime
datetime2
datetimeoffset
decimal
float
int
money
nchar
numeric
nvarchar
real
smalldatetime
smallint
smallmoney
time
tinyint
uniqueidentifier
varbinary
varchar
*/   

end;


create function dbo.FormatString(
    @format nvarchar(4000)
    ,@param1 sql_variant = null
    ,@param2 sql_variant = null
    ,@param3 sql_variant = null
    ,@param4 sql_variant = null
    ,@param5 sql_variant = null
    )
returns nvarchar(4000)
begin
/*
Tejasvi Hegde, 29-April-2017

select dbo.FormatString('some string value %s some int %s date %s','"abcd"',100,cast(getdate() as date),DEFAULT,DEFAULT)
select dbo.FormatString('some string value %s some int %s date time %s','"abcd"',100,getdate(),DEFAULT,DEFAULT)
*/

    declare @result nvarchar(4000)

    select @param1 = dbo.formatValue(@param1)
    ,@param2 = dbo.formatValue(@param2)
    ,@param3 = dbo.formatValue(@param3)
    ,@param4 = dbo.formatValue(@param4)
    ,@param5 = dbo.formatValue(@param5)

    select @param2 = cast(@param2 as nvarchar)
    EXEC xp_sprintf @result OUTPUT,@format , @param1, @param2, @param3, @param4, @param5

    return @result

end;
Tejasvi Hegde
fuente
La respuesta más sencilla para SQL Server 2008+. Mantiene las entradas como parámetros separados. Utiliza la función xp_sprintf y se puede ampliar fácilmente. xp_sprintf - docs.microsoft.com/en-us/sql/relational-databases/…
Jeff Lewis
0

esto es lo que encontré con mis experimentos usando el integrado

Función FORMATMESSAGE ()

sp_addmessage @msgnum=50001,@severity=1,@msgText='Hello %s you are #%d',@replace='replace'
SELECT FORMATMESSAGE(50001, 'Table1', 5)

cuando llama sp_addmessage, su plantilla de mensaje se almacena en la tabla del sistema master.dbo.sysmessages (verificado en SQLServer 2000).

Debe administrar la adición y eliminación de cadenas de plantilla de la tabla usted mismo, lo cual es incómodo si todo lo que realmente desea es enviar un mensaje rápido a la pantalla de resultados.

La solución proporcionada por Kathik DV parece interesante pero no funciona con SQL Server 2000, así que la modifiqué un poco y esta versión debería funcionar con todas las versiones de SQL Server:

IF OBJECT_ID( N'[dbo].[FormatString]', 'FN' ) IS NOT NULL
    DROP FUNCTION [dbo].[FormatString]
GO
/***************************************************
Object Name : FormatString
Purpose : Returns the formatted string.
Original Author : Karthik D V http://stringformat-in-sql.blogspot.com/
Sample Call:
SELECT dbo.FormatString ( N'Format {0} {1} {2} {0}', N'1,2,3' )
*******************************************/
CREATE FUNCTION [dbo].[FormatString](
@Format NVARCHAR(4000) ,
@Parameters NVARCHAR(4000)
)
RETURNS NVARCHAR(4000)
AS
BEGIN
    --DECLARE @Format NVARCHAR(4000), @Parameters NVARCHAR(4000) select @format='{0}{1}', @Parameters='hello,world'
    DECLARE @Message NVARCHAR(400), @Delimiter CHAR(1)
    DECLARE @ParamTable TABLE ( ID INT IDENTITY(0,1), Parameter VARCHAR(1000) )
    Declare @startPos int, @endPos int
    SELECT @Message = @Format, @Delimiter = ','

    --handle first parameter
     set @endPos=CHARINDEX(@Delimiter,@Parameters)
    if (@endPos=0 and @Parameters is not null) --there is only one parameter
        insert into @ParamTable (Parameter) values(@Parameters)
    else begin
        insert into @ParamTable (Parameter) select substring(@Parameters,0,@endPos)
    end

    while @endPos>0
    Begin
        --insert a row for each parameter in the 
        set @startPos = @endPos + LEN(@Delimiter)
        set @endPos = CHARINDEX(@Delimiter,@Parameters, @startPos)
        if (@endPos>0)
            insert into @ParamTable (Parameter) select substring(@Parameters,@startPos,@endPos)
        else
            insert into @ParamTable (Parameter) select substring(@Parameters,@startPos,4000)            
    End

    UPDATE @ParamTable SET @Message = REPLACE ( @Message, '{'+CONVERT(VARCHAR,ID) + '}', Parameter )
    RETURN @Message
END
Go
    grant execute,references on dbo.formatString to public

Uso:

print dbo.formatString('hello {0}... you are {1}','world,good')
--result: hello world... you are good
ValienteNuevoMatemáticas
fuente
-1

En realidad, no hay una función incorporada similar a la cadena. La función de formato de .NET está disponible en el servidor SQL.

Hay una función FORMATMESSAGE () en el servidor SQL pero imita la función printf () de C, no la función string.Format de .NET.

SELECT FORMATMESSAGE('This is the %s and this is the %s.', 'first variable', 'second variable') AS Result
Brijesh Kumar Tripathi
fuente
-2

No exactamente, pero vería algunos de los artículos sobre manejo de cadenas (entre otras cosas) de "Phil Factor" (¿geddit?) En Simple Talk.

Duncan Smart
fuente
-3

este es un mal enfoque. Debería trabajar con dll de ensamblaje, en los que hará lo mismo por usted con un mejor rendimiento.

pelegk1
fuente