Simple code. Very strange behaviour, triple checked this. #filePath and #path are NVARCHAR(260) and #exists is INT. dbo.WriteToFile writes a file to disk, in this case an empty file.
EXEC master..xp_fileexist #filePath, #exists OUTPUT
print #exists
IF #exists = 0
BEGIN
EXEC dbo.WriteToFile N'', #path, #filename
RAISERROR('A', 20, -1) WITH log
END
RAISERROR('B', 20, -1) WITH log
When I run this code for the first time and #exists is 0, it goes into the if block. File is created as expected. Confirmed that. But RAISERROR in the if block is not called. Instead only the RAISERROR outside the if block is called.
But if I replace the first RAISERROR ('A') with PRINT 'blabla' the print statement is called as expected.
If I replace #exists = 0 with 1=1 and keep the first RAISERROR ('A') as is, again everything behaves correctly and the first RAISERROR ('A') is called.
Can someone explain this?
Oh yeah, and why does
print 'bla'
raiserror('bla', 20, -1) with log
in a brand new query window get me this:
bla
Meldung 2745, Ebene 16, Status 2, Zeile 3
Der Prozess mit der ID 54 hat den Benutzerfehler 50000, Schweregrad 20, ausgelöst. Dieser Prozess wird von SQL Server beendet.
bla
Meldung 2745, Ebene 16, Status 2, Zeile 3
Der Prozess mit der ID 54 hat den Benutzerfehler 50000, Schweregrad 20, ausgelöst. Dieser Prozess wird von SQL Server beendet.
This is 2 times the same message. Maybe my SQL server is configured incorrectly?
I will post the WriteToFile procedure too if you are interested, maybe it helps, I have this procedure from the web, but it works perfectly, but maybe it produces the RAISERROR behaviour:
ALTER PROCEDURE dbo.WriteToFile
(
#String Varchar(max), --8000 in SQL Server 2000
#Path VARCHAR(255),
#Filename VARCHAR(100)
)
AS
BEGIN
DECLARE #objFileSystem int
,#objTextStream int,
#objErrorObject int,
#strErrorMessage Varchar(1000),
#Command varchar(1000),
#hr int,
#fileAndPath varchar(80)
select #strErrorMessage='opening the File System Object'
EXECUTE #hr = sp_OACreate 'Scripting.FileSystemObject' , #objFileSystem OUT
Select #FileAndPath=#path+'\'+#filename
if #HR=0 Select #objErrorObject=#objFileSystem , #strErrorMessage='Creating file "'+#FileAndPath+'"'
if #HR=0 execute #hr = sp_OAMethod #objFileSystem , 'CreateTextFile'
, #objTextStream OUT, #FileAndPath,2,True
if #HR=0 Select #objErrorObject=#objTextStream,
#strErrorMessage='writing to the file "'+#FileAndPath+'"'
if #HR=0 execute #hr = sp_OAMethod #objTextStream, 'Write', Null, #String
if #HR=0 Select #objErrorObject=#objTextStream, #strErrorMessage='closing the file "'+#FileAndPath+'"'
if #HR=0 execute #hr = sp_OAMethod #objTextStream, 'Close'
if #hr<>0
begin
Declare
#Source varchar(255),
#Description Varchar(255),
#Helpfile Varchar(255),
#HelpID int
EXECUTE sp_OAGetErrorInfo #objErrorObject,
#source output,#Description output,#Helpfile output,#HelpID output
Select #strErrorMessage='Error whilst '
+coalesce(#strErrorMessage,'doing something')
+', '+coalesce(#Description,'')
raiserror (#strErrorMessage,16,1)
end
EXECUTE sp_OADestroy #objTextStream
EXECUTE sp_OADestroy #objTextStream
END
edit: running SQL Server 2008 R2
I dont know if you really need a "20" error level, but please try with:
RAISERROR('A', 16, -1) WITH log
DECLARE #str VARCHAR (MAX);
SELECT #str = COALESCE(#str + CHAR(10), '') +
'EXECUTE CreateDeno ' + CAST(ID AS VARCHAR)
FROM GL_To_Batch_Details
WHERE TYPE = 'C' AND
Deno_ID IS NULL;
--PRINT #str;--SELECT #str;
**EXEC(#str);**
EDITED
Does EXECUTE statement truncate strings to 8,000 chars like PRINT? How can I execute a dynamic SQL statement having more than 8,000 chars?
Any suggestion would be warmly appreciated.
PRINT is limited to 8k in output.
There is also an 8k limit in SSMS results pane.
Go to
tools -> options -> query results
to see the options.
To verify the length of the actual data, check:
SELECT LEN(#str)
When concatenating strings and the result is of type VARCHAR(MAX) and is over 8000 characters, at least one parameter and/or element being used in the concatenation need to be of the VARCHAR(MAX) type otherwise truncation will occur in the resultant string and will not be executable in an EXEC statement.
Example:
DECLARE #sql AS VARCHAR(MAX);
/* DECLARE #someItem AS VARCHAR(100); -- WILL CAUSE TRUNCATION WHEN #sql HAS LEN > 8000 */
DECLARE #someItem AS VARCHAR(MAX); -- All string variables need to be VARCHAR(MAX) when concatenating to another VARCHAR(MAX)
SET #someItem = 'Just assume the resulting #sql variable goes over 8000 characters...';
SET #sql = 'SELECT Something FROM Somewhere WHERE SomeField = ''' + #someItem + '''';
EXEC (#sql);
--PRINT #sql;
More information on MSDN.
"If the result of the concatenation of strings exceeds the limit of
8,000 bytes, the result is truncated. However, if at least one of the
strings concatenated is a large value type, truncation does not
occur."
The default length of a varchar is 30 characters:
CAST (ID AS VARCHAR)
Is it possible that id is longer than 30 characters?
The PRINT command is certainly limited to 8000 chars, irrespective of the length of the output (or whether it is varchar(max)). To work around this you need to output the string in chunks of <8000 chars
Update: In answer to your edit, exec doesn't limit the string length. I've put together the following example to show this:
DECLARE #str VARCHAR (MAX);
;WITH CTE_Count AS
(
select counter = 1
union all
select counter = counter+1
from CTE_Count
Where counter < 2000
)
SELECT
#str=COALESCE(#str + CHAR (10) ,
'' ) + 'select value=' + CAST (counter AS VARCHAR)
from
CTE_Count
Option (MAXRECURSION 0)
PRINT len(#str);--SELECT #str;
exec (#str)
Running this prints the length as 34892 chars, and all 2000 execute statements do run (be warned, it may take a few mins!)
It happens when you concatenate literals if one is not a varchar(max) the result ill be "implicit casted" to varchar(8000).
To generate a literal varchar(max) all parts must be varchar(max).
Note: It happened to me doing updates on varchar(max) columns, never tested with the EXEC command.
Also as noted in previous answers the print command holds a limit but you can try selecting that variable instead of printing it. (also ther's a limit on that select length you can configure on MS-SMS)
I also wanted to see what I was sending to Exec, and was confused by the PRINT limit. Had to write a proc to print in chunks.
CREATE PROCEDURE [dbo].[KFX_PrintVarcharMax]
#strMax varchar(max)
AS
BEGIN
SET NOCOUNT ON;
DECLARE
#index int = 0,
#start int = 1,
#blkSize int = 2000;
WHILE #Start < LEN(#strMax)
BEGIN
IF #start + #blkSize >= LEN(#strMax)
BEGIN
-- If remainder is less than blocksize print the remainder, and exit.
PRINT SUBSTRING(#strMax, #start, #blkSize)
BREAK;
END
-- Else find the next terminator (beyond the blksize)
SET #index = CHARINDEX(CHAR(10), #strMax, #start + #blkSize);
if #index >= #start
BEGIN
PRINT SubString(#strMax, #start, #index - #start + 1)
SET #start = #index + 1;
SET #blkSize = CASE WHEN #start + 2000 < LEN(#strMax) THEN 2000
ELSE LEN(#strMax) - #start + 1 END
END
ELSE
BEGIN
-- No char(10) found. Just print the rest.
PRINT SUBSTRING(#strMax, #start, LEN(#strMax))
BREAK;
END
END
END
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.