Try-Catch in User Defined Function? - tsql

I'm trying to write a UDF to translate a string that is either a guid or a project code associated with that guid into the guid:
CREATE FUNCTION fn_user_GetProjectID
(
#Project nvarchar(50)
)
RETURNS uniqueidentifier
AS
BEGIN
declare #ProjectID uniqueidentifier
BEGIN TRY
set #ProjectID = cast(#Project as uniqueidentifier)
END TRY
BEGIN CATCH
set #ProjectID = null
END CATCH
if(#ProjectID is null)
BEGIN
select #ProjectID = ProjectID from Project where projectcode = #Project
END
return #ProjectID
END
This works fine if the above code is embedded in my Stored Procedures, but I'd like to make a function out of it so that I follow DRY.
When I try to create the Function, I get errors like this:
Msg 443, Level 16, State 14, Procedure fn_user_GetProjectID, Line 16
Invalid use of side-effecting or time-dependent operator in 'BEGIN TRY' within a function.
Does anyone have an idea how I can get around this error?
Edit: I know I can't use Try-Catch in a Function, I guess a simplified questions would be, is there a way to do a cast that will just return NULL if the cast fails, instead of an error?

Apparently you can't use TRY-CATCH in a UDF.
According to this bug-reporting page for SQL Server:
Books Online documents this behaviour,
in topic "CREATE FUNCTION
(Transact-SQL)": "The following
statements are valid in a function:
[...] Control-of-Flow statements
except TRY...CATCH statements. [...]"
But they were giving hope for the future back in 2006:
However, this is a severe limitation
that should be removed in a future
release. You should post a suggestion
in this regard and I will
wholeheartedly vote for it.

From MSDN:
A column or local variable of
uniqueidentifier data type can be
initialized to a value in the
following ways:
By using the NEWID function.
By converting from a string constant
in the form
xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx,
in which each x is a hexadecimal digit
in the range 0-9 or a-f.
For example,
6F9619FF-8B86-D011-B42D-00C04FC964FF
is a valid uniqueidentifier value.
You can use pattern matching to verify the string. Note that this won't work for specific encoding that reduces the size of the GUID:
declare #Project nvarchar(50)
declare #ProjectID uniqueidentifier
declare #HexPattern nvarchar(268)
set #HexPattern =
'[A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9]' +
'[A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9]' +
'[A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9]' +
'[A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9][A-F0-9]'
/* Take into account GUID can have curly-brackets or be missing dashes */
/* Note: this will not work for GUIDs that have been specially encoded */
set #Project = '{' + CAST(NEWID() AS VARCHAR(36)) + '}'
select #Project
set #Project = REPLACE(REPLACE(REPLACE(#Project,'{',''),'}',''),'-','')
/* Cast as uniqueid if pattern matches, otherwise return null */
if #Project LIKE #HexPattern
select #ProjectID = CAST(
SUBSTRING(#Project,1,8) + '-' +
SUBSTRING(#Project,9,4) + '-' +
SUBSTRING(#Project,13,4) + '-' +
SUBSTRING(#Project,17,4) + '-' +
SUBSTRING(#Project,21,LEN(#Project)-20)
AS uniqueidentifier)
select #ProjectID

I know I can't use Try-Catch in a Function, I guess a simplified questions would be, is there a way to do a cast that will just return NULL if the cast fails, instead of an error?
Starting from SQL Server 2012 you could use TRY_CAST/TRY_CONVERT functions:
Returns a value cast to the specified data type if the cast succeeds; otherwise, returns null.
CREATE FUNCTION fn_user_GetProjectID(#Project nvarchar(50))
RETURNS uniqueidentifier
AS
BEGIN
declare #ProjectID uniqueidentifier = TRY_CAST(#Project as uniqueidentifier);
IF(#ProjectID is null)
BEGIN
select #ProjectID = ProjectID from Project where projectcode = #Project;
END
return #ProjectID;
END

Not sure, but why not flip it around... at first glance I would simplify it like this:
select #ProjectID =
ISNULL((select ProjectID from Project where
projectcode = #Project)
,(cast #Project as uniqueidentifier))
If this doesn't provide enough error handling, I'm sure there's a better way to pre-check that the cast can work without using try/catch...

My brute force method was to create my own ToGuid() function that verifies it can be converted to a GUID first, if not, it returns null. It may not be very fast but it does the job, and it is probably faster to convert the guid if it is one than to try to look it up in the table. EDIT: I meant to give credit to this blog, where I got the basis of my code for this function: http://jesschadwick.blogspot.com/2007/11/safe-handling-of-uniqueidentifier-in.html
CREATE FUNCTION [dbo].[ToGuid]
(
#input NVARCHAR(MAX)
)
RETURNS uniqueidentifier
AS
BEGIN
DECLARE #isValidGuid BIT;
DECLARE #temp NVARCHAR(MAX);
SET #isValidGuid = 1;
SET #temp = UPPER(LTRIM(RTRIM(REPLACE(REPLACE(REPLACE(#input, '-', ''), '{', ''), '}', ''))));
IF(#temp IS NOT NULL AND LEN(#temp) = 32)
BEGIN
DECLARE #index INT;
SET #index = 1
WHILE (#index <= 32)
BEGIN
IF (SUBSTRING(#temp, #index, 1) IN ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'A', 'B', 'C', 'D', 'E', 'F'))
BEGIN
SET #index = #index + 1
END
ELSE
BEGIN
SET #isValidGuid = 0
BREAK;
END
END
END
ELSE
BEGIN
SET #isValidGuid = 0
END
DECLARE #ret UNIQUEIDENTIFIER
IF(#isValidGuid = 1)
set #ret = cast(#input AS UNIQUEIDENTIFIER)
ELSE
set #ret = NULL
RETURN #ret
END
I'm still very interested if there is a better answer than this.

Verify if #Project is a number using the ISNUMERIC function.
your code should looks like that:
declare #ProjectID uniqueidentifier
set #ProjectID = null
IF ISNUMERIC(#Project) > 0
BEGIN
set #ProjectID = cast(#Project as uniqueidentifier)
END
if(#ProjectID is null)
BEGIN
select #ProjectID = ProjectID from Project where projectcode = #Project
END
return #ProjectID

Related

TSQL Replace Doubled Characters

Let's say I have data:
heloo
cuube
triniity
How to write script that will replace those "doubled" characters with only one? So the result from the above data set would be:
helo
cube
trinity
Usually I post some script where I tried to achieve this, but this time I can't think of any.
This should work:
CREATE PROCEDURE remove_duplicate_characters(#string VARCHAR(100))
AS
DECLARE #result VARCHAR(100)
SET #result=''
SELECT #result=#result+MIN(SUBSTRING(#string ,number,1)) FROM
(
SELECT number FROM master..spt_values WHERE type='p' AND number BETWEEN 1 AND len(#string )) AS t GROUP BY SUBSTRING(#string,number,1) ORDER BY MIN(number)
)
SELECT #result
GO
You then call it like this:
EXEC remove_duplicate_characters 'heloo'
Source
This script does not depend on having access to master functions, and just relies on t-sql string functions.
declare #word varchar(100) = 'aaaacuuuuuubeeeee', #result varchar(100) = ''
declare #letter char, #idx int = 0, #lastletter char = ''
while(#idx <= len(#word))
begin
select #letter = substring(#word,#idx,1)
if (#letter != #lastletter)
begin
select #result = concat(#result,#letter)
end
select #lastletter = #letter,#idx = #idx + 1
end
select #result

comma separated value in sql statement from a variable

I am getting comma separated value like this in a variable (let say variable name #listobj)
'abc' , 'xyz'
but when I am using below statement it is not giving me the correct result
SELECT * FROM someTable
Where column1 IN (#listobj)
but abc is present in the table.
Where I am doing it wrong?
create a function that split the string to
CREATE FUNCTION dbo.Split(#String varchar(8000), #Delimiter char(1))
returns #temptable TABLE (items varchar(8000))
as
begin
declare #idx int
declare #slice varchar(8000)
select #idx = 1
if len(#String)<1 or #String is null return
while #idx!= 0
begin
set #idx = charindex(#Delimiter,#String)
if #idx!=0
set #slice = left(#String,#idx - 1)
else
set #slice = #String
if(len(#slice)>0)
insert into #temptable(Items) values(#slice)
set #String = right(#String,len(#String) - #idx)
if len(#String) = 0 break
end
return
end
then make call to the function SELECT * FROM someTable
Where column1 IN (dbo.Split(#listobj))
enter link description here
SQLFiddle demo
select * from someTable
where ','+LTRIM(RTRIM(#listobj))+',' LIKE '%,'+LTRIM(RTRIM(column1))+',%'
A classic question and the answer is no, you cannot use a parameter in this way. There are several workarounds though
One of which is to parse the value inside the stored procedure and dynamically generate sql inside the procedure to be execute later. However, this is not a good practice.
Refer to this question
How to pass a comma separated list to a stored procedure?
and also some good discussion on it here
http://social.msdn.microsoft.com/Forums/en-US/sqlintegrationservices/thread/1ccdd39e-8d58-45b2-9c21-5c4dbd857f95/

How to use a T-SQL UDF in a SELECT or UPDATE statement?

Context: SQL Server 2000
I've written a UDF that gives me the text between two other texts, viz
CREATE FUNCTION dbo.StrBetween
(
#Text nvarchar(4000),
#Lhs nvarchar(4000),
#Rhs nvarchar(4000)
)
RETURNS nvarchar(4000)
AS
BEGIN
DECLARE #LhsOffset INT;
DECLARE #RhsOffset INT;
DECLARE #Result NVARCHAR(4000);
SET #LhsOffset = CHARINDEX( #Lhs, #Text );
IF #LhsOffset = 0
BEGIN
RETURN #Text;
END
SET #Result = SUBSTRING( #Text, #LhsOffset+1, LEN(#Text)-LEN(#Lhs));
SET #RhsOffset = CHARINDEX( #Rhs, #Result );
IF #RhsOffset = 0
BEGIN
RETURN #Result;
END
SET #Result = SUBSTRING( #Result, 1, #RhsOffset - 1 );
RETURN #Result;
END
This works fine in SQL Query Analyser if I have, say,
SELECT dbo.StrBetween('dog','d','g')
However, when I pass a column in as the value of the first argument, I get no response. For example,
SELECT [TEST].[dbo].StrBetween(Referrer,'//', '/') as tst FROM tblTest
Referrer is declared as an nvarchar field. I'm a newbie when it comes to T-SQL. What obvious thing am I not seeing?
It's not an issue with calling - it's a logic issue, and the fact that your #Rhs value is part of the #Lhs value.
SET #Result = SUBSTRING( #Text, #LhsOffset+1, LEN(#Text)-LEN(#Lhs));
This is removing the first character of your #Lhs string. However, since the second character is /, and that's what your #Rhs match is searching for, it immediately finds it at position 1 and so you get an empty string.
Instead, try:
SET #Result = SUBSTRING( #Text, #LhsOffset+LEN(#Lhs), 4000);
You don't have to be exact with computing a length. If you ask for 4000 characters and the string is only 12 characters long, SUBSTRING will give you back at most 12 characters. So don't bother computing the new length.

TSQL How do you output PRINT in a user defined function?

Basically I want to use PRINT statement inside a user defined function to aide my debugging.
However I'm getting the following error;
Invalid use of side-effecting or time-dependent operator in 'PRINT'
within a function.
Can this not be done?
Anyway to aid my user defined function debugging?
Tip:
generate error.
declare #Day int, #Config_Node varchar(50)
set #Config_Node = 'value to trace'
set #Day = #Config_Node
You will get this message:
Conversion failed when converting the varchar value 'value to trace'
to data type int.
No, sorry. User-defined functions in SQL Server are really limited, because of a requirement that they be deterministic. No way round it, as far as I know.
Have you tried debugging the SQL code with Visual Studio?
I got around this by temporarily rewriting my function to something like this:
IF OBJECT_ID ('[dbo].[fx_dosomething]', 'TF') IS NOT NULL
drop function [dbo].[fx_dosomething];
GO
create FUNCTION dbo.fx_dosomething ( #x numeric )
returns #t table (debug varchar(100), x2 numeric)
as
begin
declare #debug varchar(100)
set #debug = 'printme';
declare #x2 numeric
set #x2 = 0.123456;
insert into #t values (#debug, #x2)
return
end
go
select * from fx_dosomething(0.1)
I have tended in the past to work on my functions in two stages. The first stage would be to treat them as fairly normal SQL queries and make sure that I am getting the right results out of it. After I am confident that it is performing as desired, then I would convert it into a UDF.
Use extended procedure xp_cmdshell to run a shell command. I used it to print output to a file:
exec xp_cmdshell 'echo "mytextoutput" >> c:\debuginfo.txt'
This creates the file debuginfo.txt if it does not exist. Then it adds the text "mytextoutput" (without quotation marks) to the file. Any call to the function will write an additional line.
You may need to enable this db-server property first (default = disabled), which I realize may not be to the liking of dba's for production environments though.
No, you can not.
You can call a function from a stored procedure and debug a stored procedure (this will step into the function)
On my opinion, whenever I want to print or debug a function. I will copy the content of it to run as a normal SQL script. For example
My function:
create or alter function func_do_something_with_string(#input nvarchar(max)) returns nvarchar(max)
as begin
-- some function logic content
declare #result nvarchar(max)
set #result = substring(#input , 1, 10)
-- or do something else
return #result
end
Then I just copy and run this out of the function to debug
declare #input nvarchar(max) = 'Some string'
-- some function logic content
declare #result nvarchar(max)
set #result = substring(#input , 1, 10)
-- this line is added to check while debugging
print #result
-- or do something else
-- print the final result
print #result
You can try returning the variable you wish to inspect.
E.g. I have this function:
--Contencates seperate date and time strings and converts to a datetime. Date should be in format 25.03.2012. Time as 9:18:25.
ALTER FUNCTION [dbo].[ufn_GetDateTime] (#date nvarchar(11), #time nvarchar(11))
RETURNS datetime
AS
BEGIN
--select dbo.ufn_GetDateTime('25.03.2012.', '9:18:25')
declare #datetime datetime
declare #day_part nvarchar(3)
declare #month_part nvarchar(3)
declare #year_part nvarchar(5)
declare #point_ix int
set #point_ix = charindex('.', #date)
set #day_part = substring(#date, 0, #point_ix)
set #date = substring(#date, #point_ix, len(#date) - #point_ix)
set #point_ix = charindex('.', #date)
set #month_part = substring(#date, 0, #point_ix)
set #date = substring(#date, #point_ix, len(#date) - #point_ix)
set #point_ix = charindex('.', #date)
set #year_part = substring(#date, 0, #point_ix)
set #datetime = #month_part + #day_part + #year_part + ' ' + #time
return #datetime
END
When I run it.. I get:
Msg 241, Level 16, State 1, Line 1
Conversion failed when converting date and/or time from character string.
Arghh!!
So, what do I do?
ALTER FUNCTION [dbo].[ufn_GetDateTime] (#date nvarchar(11), #time nvarchar(11))
RETURNS nvarchar(22)
AS
BEGIN
--select dbo.ufn_GetDateTime('25.03.2012.', '9:18:25')
declare #day_part nvarchar(3)
declare #point_ix int
set #point_ix = charindex('.', #date)
set #day_part = substring(#date, 0, #point_ix)
return #day_part
END
And I get '25'. So, I am off by one and so I change to..
set #day_part = substring(#date, 0, #point_ix + 1)
Voila! Now it works :)

String.Format like functionality in T-SQL?

I'm looking for a built-in function/extended function in T-SQL for string manipulation similar to the String.Format method in .NET.
If you are using SQL Server 2012 and above, you can use FORMATMESSAGE. eg.
DECLARE #s NVARCHAR(50) = 'World';
DECLARE #d INT = 123;
SELECT FORMATMESSAGE('Hello %s, %d', #s, #d)
-- RETURNS 'Hello World, 123'
More examples from 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');
NOTES:
Undocumented in 2012
Limited to 2044 characters
To escape the % sign, you need to double it.
If you are logging errors in extended events, calling FORMATMESSAGE comes up as a (harmless) error
take a look at xp_sprintf. example below.
DECLARE #ret_string varchar (255)
EXEC xp_sprintf #ret_string OUTPUT,
'INSERT INTO %s VALUES (%s, %s)', 'table1', '1', '2'
PRINT #ret_string
Result looks like this:
INSERT INTO table1 VALUES (1, 2)
Just found an issue with the max size (255 char limit) of the string with this so there is an alternative function you can use:
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
To get the same result as above you call the function as follows:
print dbo.fnSprintf('INSERT INTO %s VALUES (%s, %s)', 'table1,1,2', default)
I have created a user defined function to mimic the string.format functionality.
You can use it.
stringformat-in-sql
UPDATE:
This version allows the user to change the delimitter.
-- DROP function will loose the security settings.
IF object_id('[dbo].[svfn_FormatString]') IS NOT NULL
DROP FUNCTION [dbo].[svfn_FormatString]
GO
CREATE FUNCTION [dbo].[svfn_FormatString]
(
#Format NVARCHAR(4000),
#Parameters NVARCHAR(4000),
#Delimiter CHAR(1) = ','
)
RETURNS NVARCHAR(MAX)
AS
BEGIN
/*
Name: [dbo].[svfn_FormatString]
Creation Date: 12/18/2020
Purpose: Returns the formatted string (Just like in C-Sharp)
Input Parameters: #Format = The string to be Formatted
#Parameters = The comma separated list of parameters
#Delimiter = The delimitter to be used in the formatting process
Format: #Format = N'Hi {0}, Welcome to our site {1}. Thank you {0}'
#Parameters = N'Karthik,google.com'
#Delimiter = ','
Examples:
SELECT dbo.svfn_FormatString(N'Hi {0}, Welcome to our site {1}. Thank you {0}', N'Karthik,google.com', default)
SELECT dbo.svfn_FormatString(N'Hi {0}, Welcome to our site {1}. Thank you {0}', N'Karthik;google.com', ';')
*/
DECLARE #Message NVARCHAR(400)
DECLARE #ParamTable TABLE ( Id INT IDENTITY(0,1), Paramter VARCHAR(1000))
SELECT #Message = #Format
;WITH CTE (StartPos, EndPos) AS
(
SELECT 1, CHARINDEX(#Delimiter, #Parameters)
UNION ALL
SELECT EndPos + (LEN(#Delimiter)), CHARINDEX(#Delimiter, #Parameters, EndPos + (LEN(#Delimiter)))
FROM CTE
WHERE EndPos > 0
)
INSERT INTO #ParamTable ( Paramter )
SELECT
[Id] = SUBSTRING(#Parameters, StartPos, CASE WHEN EndPos > 0 THEN EndPos - StartPos ELSE 4000 END )
FROM CTE
UPDATE #ParamTable
SET
#Message = REPLACE(#Message, '{'+ CONVERT(VARCHAR, Id) + '}', Paramter )
RETURN #Message
END
There is a way, but it has its limitations. You can use the FORMATMESSAGE() function. It allows you to format a string using formatting similar to the printf() function in C.
However, the biggest limitation is that it will only work with messages in the sys.messages table. Here's an article about it: microsoft_library_ms186788
It's kind of a shame there isn't an easier way to do this, because there are times when you want to format a string/varchar in the database. Hopefully you are only looking to format a string in a standard way and can use the sys.messages table.
Coincidentally, you could also use the RAISERROR() function with a very low severity, the documentation for raiseerror even mentions doing this, but the results are only printed. So you wouldn't be able to do anything with the resulting value (from what I understand).
Good luck!
Raw t-sql is limited to CHARINDEX(), PATINDEX(), REPLACE(), and SUBSTRING() for string manipulation. But with sql server 2005 and later you can set up user defined functions that run in .Net, which means setting up a string.format() UDF shouldn't be too tough.
I think there is small correction while calculating end position.
Here is correct function
**>>**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
One more idea.
Although this is not a universal solution - it is simple and works, at least for me :)
For one placeholder {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
For two placeholders {0} and {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
For three placeholders {0}, {1} and {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
and so on...
Such an approach allows us to use these functions in SELECT statement and with parameters of nvarchar, number, bit and datetime datatypes.
For example:
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);
Actually there is no built in function similar to string.Format function of .NET is available in SQL server.
There is a function FORMATMESSAGE() in SQL server but it mimics to printf() function of C not string.Format function of .NET.
SELECT FORMATMESSAGE('This is the %s and this is the %s.', 'first variable', 'second variable') AS Result
Here is my version. Can be extended to accommodate more number of parameters and can extend formatting based on type. Currently only date and datetime types are formatted.
Example:
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)
Output:
some string "abcd" some int 100 date 29-Apr-2017
some string "abcd" some int 100 date time 29-Apr-2017 19:40
Functions:
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;
here's what I found with my experiments using the built-in
FORMATMESSAGE() function
sp_addmessage #msgnum=50001,#severity=1,#msgText='Hello %s you are #%d',#replace='replace'
SELECT FORMATMESSAGE(50001, 'Table1', 5)
when you call up sp_addmessage, your message template gets stored into the system table master.dbo.sysmessages (verified on SQLServer 2000).
You must manage addition and removal of template strings from the table yourself, which is awkward if all you really want is output a quick message to the results screen.
The solution provided by Kathik DV, looks interesting but doesn't work with SQL Server 2000, so i altered it a bit, and this version should work with all versions of 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
Usage:
print dbo.formatString('hello {0}... you are {1}','world,good')
--result: hello world... you are good
At the moment this doesn't really exist (although you can of course write your own). There is an open connect bug for it: https://connect.microsoft.com/SQLServer/Feedback/Details/3130221, which as of this writing has just 1 vote.
Not exactly, but I would check out some of the articles on string handling (amongst other things) by "Phil Factor" (geddit?) on Simple Talk.
this is bad approach. you should work with assembly dll's, in which will do the same for you with better performance.