String.Format jak funkcjonalność w T-SQL?

90

Szukam funkcji wbudowanej / rozszerzonej w T-SQL do manipulacji ciągami podobnymi do String.Formatmetody w .NET.

Jeff
źródło

Odpowiedzi:

70

Jeśli używasz programu SQL Server 2012 i nowszych wersji, możesz użyć FORMATMESSAGE. na przykład.

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

Więcej przykładów z 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');

UWAGI:

  • Nieudokumentowane w 2012 roku
  • Ograniczone do 2044 znaków
  • Aby uciec przed znakiem%, musisz go podwoić.
  • Jeśli rejestrujesz błędy w rozszerzonych zdarzeniach, wywołanie FORMATMESSAGEbędzie (nieszkodliwe)
g2server
źródło
1
Jeśli używasz SQL 2012, możesz użyć funkcji FORMAT bez wszystkich powyższych komplikacji :)
Reversed Engineer
1
To powinno mieć znacznie więcej głosów! Zawsze unikano niesamowitego znaleziska, ponieważ zakładano, że będzie działać tylko z wbudowanym msg_number.
Lankymart,
1
@Lankymart, bump! Zgadzam się, że to powinna być akceptowana odpowiedź: prosta, wbudowana i świetnie działająca.
Robert Synoradzki
5
@bijayk akceptuje tylko określone nazwy zastępcze, np.% s dla łańcuchów,% i dla int.
g2server
2
@lostmylogin W string.FormatT-SQL nie ma funkcji stylu, to jest najbliższa, jaką otrzymasz.
Ian Kemp
53

spójrz na xp_sprintf . przykład poniżej.

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

Wynik wygląda następująco:

INSERT INTO table1 VALUES (1, 2)

Właśnie znalazłem problem z maksymalnym rozmiarem (limit 255 znaków) w ciągu, więc istnieje alternatywna funkcja, której możesz użyć:

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

Aby uzyskać taki sam wynik, jak powyżej, wywołaj funkcję w następujący sposób:

print dbo.fnSprintf('INSERT INTO %s VALUES (%s, %s)', 'table1,1,2', default)
Josh
źródło
7
Tylko do Twojej wiadomości, jeśli którykolwiek z parametrów zawiera przecinek, to nie masz szczęścia. Jeśli zdarzy ci się przypadkowo zdać jeden z nich, będziesz miał problemy ze zrozumieniem, co poszło nie tak.
Kyle
15

Utworzyłem funkcję zdefiniowaną przez użytkownika, aby naśladować funkcjonalność string.format. Możesz tego użyć.

stringformat-in-sql

Karthik DV
źródło
Podoba mi się to rozwiązanie, ponieważ mam zastrzeżenia do używania funkcji xp_ / SP w produkcji. Użyłem twojego kodu jako podstawy i pozwoliłem na przekazanie separatora, co eliminuje wszelkie obawy o użycie przecinków w danych.
Tim Friesen
4

Jest sposób, ale ma on swoje ograniczenia. Możesz użyć FORMATMESSAGE()funkcji. Pozwala sformatować ciąg przy użyciu formatowania podobnego do printf()funkcji w C.

Jednak największym ograniczeniem jest to, że będzie działać tylko z wiadomościami w tabeli sys.messages. Oto artykuł na ten temat: microsoft_library_ms186788

Szkoda, że ​​nie ma prostszego sposobu na zrobienie tego, ponieważ są chwile, kiedy chcesz sformatować ciąg / varchar w bazie danych. Mam nadzieję, że chcesz sformatować ciąg tylko w standardowy sposób i możesz użyć sys.messagestabeli.

Przypadkowo możesz również użyć RAISERROR()funkcji z bardzo niską istotnością, dokumentacja podniesienia błędu nawet wspomina o tym, ale wyniki są tylko drukowane. Więc nie byłbyś w stanie nic zrobić z uzyskaną wartością (z tego, co rozumiem).

Powodzenia!

jj.
źródło
1
Rozumiem, że jest stary, ale odpowiedź o założenie FORMATMESSAGE()jest błędne, jednak zrozumiałe, ponieważ jest nieudokumentowane, ale będzie przyjmować dowolny ciąg jako pierwszy parametr, zobacz tę odpowiedź od @ g2server .
Lankymart,
@Lankymart Masz rację - to stara odpowiedź. Możliwość akceptowania ciągu znaków została dodana dopiero w SQL 2012.
jj.
3

Surowe t-sql jest ograniczone do CHARINDEX (), PATINDEX (), REPLACE () i SUBSTRING () do manipulacji na ciągach. Ale z sql server 2005 i późniejszymi możesz skonfigurować funkcje zdefiniowane przez użytkownika, które działają w .Net, co oznacza, że ​​ustawienie string.format () UDF nie powinno być zbyt trudne.

Joel Coehoorn
źródło
2

Jeszcze jeden pomysł.

Chociaż nie jest to rozwiązanie uniwersalne - jest proste i działa, przynajmniej dla mnie :)

Dla jednego symbolu zastępczego {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

W przypadku dwóch symboli zastępczych {0} i {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

W przypadku trzech symboli zastępczych {0}, {1} i {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

i tak dalej...

Takie podejście pozwala nam używać tych funkcji w instrukcji SELECT oraz z parametrami typu nvarchar, number, bit i datetime.

Na przykład:

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
źródło
1

Myślę, że przy obliczaniu pozycji końcowej jest mała korekta.

Oto poprawna funkcja

**>>**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
źródło
1

Oto moja wersja. Można rozszerzyć, aby pomieścić większą liczbę parametrów i rozszerzyć formatowanie w oparciu o typ. Obecnie formatowane są tylko typy daty i godziny.

Przykład:

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)

Wynik:

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

Funkcje:

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
źródło
Najprostsza odpowiedź dla SQL Server 2008+. Zachowuje dane wejściowe jako oddzielne parametry. Używa funkcji xp_sprintf i można ją łatwo rozszerzyć. xp_sprintf - docs.microsoft.com/en-us/sql/relational-databases/ ...
Jeff Lewis,
0

oto, co znalazłem podczas moich eksperymentów z użyciem wbudowanego

FORMATMESSAGE ()

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

kiedy wywołujesz sp_addmessage, szablon wiadomości zostaje zapisany w tabeli systemowej master.dbo.sysmessages (zweryfikowany na SQLServer 2000).

Musisz samodzielnie zarządzać dodawaniem i usuwaniem ciągów szablonów z tabeli, co jest niewygodne, jeśli naprawdę chcesz tylko wyświetlić szybką wiadomość na ekranie wyników.

Rozwiązanie dostarczone przez Kathik DV wygląda interesująco, ale nie działa z SQL Server 2000, więc trochę je zmieniłem i ta wersja powinna działać ze wszystkimi wersjami 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

Stosowanie:

print dbo.formatString('hello {0}... you are {1}','world,good')
--result: hello world... you are good
BraveNewMath
źródło
-1

Właściwie nie ma wbudowanej funkcji podobnej do stringa. Funkcja formatowania .NET jest dostępna w serwerze SQL.

Istnieje funkcja FORMATMESSAGE () na serwerze SQL, ale naśladuje ona funkcję printf () w języku C, a nie funkcję string.Format w .NET.

SELECT FORMATMESSAGE('This is the %s and this is the %s.', 'first variable', 'second variable') AS Result
Brijesh Kumar Tripathi
źródło
-2

Niezupełnie, ale chciałbym sprawdzić niektóre artykuły na temat obsługi ciągów (między innymi) przez „Phil Factor” (geddit?) Na Simple Talk.

Duncan Smart
źródło
-3

to jest złe podejście. powinieneś pracować z bibliotekami DLL assemblera, w których zrobisz to samo z lepszą wydajnością.

pelegk1
źródło