How to print VARCHAR(MAX) using Print Statement? - sql
I have a code which is:
DECLARE #Script VARCHAR(MAX)
SELECT #Script = definition FROM manged.sys.all_sql_modules sq
where sq.object_id = (SELECT object_id from managed.sys.objects
Where type = 'P' and Name = 'usp_gen_data')
Declare #Pos int
SELECT #pos=CHARINDEX(CHAR(13)+CHAR(10),#script,7500)
PRINT SUBSTRING(#Script,1,#Pos)
PRINT SUBSTRING(#script,#pos,8000)
The length of the Script is around 10,000 Characters and Since I am using print Statement which can hold only max of 8000. So I am using two print statements.
The problem is when I have a script which is of say 18000 characters then I used to use 3 print statements.
So Is there a way that I could set the number of print statements depending on the length of the script?
I know it's an old question, but what I did is not mentioned here.
For me the following worked (for up to 16k chars)
DECLARE #info NVARCHAR(MAX)
--SET #info to something big
PRINT CAST(#info AS NTEXT)
If you have more than 16k chars you can combine with #Yovav's answer like this (64k should be enough for anyone ;)
print cast( substring(#info, 1, 16000) as ntext )
print cast( substring(#info, 16001, 32000) as ntext )
print cast( substring(#info, 32001, 48000) as ntext )
print cast( substring(#info, 48001, 64000) as ntext )
The following workaround does not use the PRINT statement. It works well in combination with the SQL Server Management Studio.
SELECT CAST('<root><![CDATA[' + #MyLongString + ']]></root>' AS XML)
You can click on the returned XML to expand it in the built-in XML viewer.
There is a pretty generous client side limit on the displayed size. Go to Tools/Options/Query Results/SQL Server/Results to Grid/XML data to adjust it if needed.
Here is how this should be done:
DECLARE #String NVARCHAR(MAX);
DECLARE #CurrentEnd BIGINT; /* track the length of the next substring */
DECLARE #offset tinyint; /*tracks the amount of offset needed */
set #string = replace( replace(#string, char(13) + char(10), char(10)) , char(13), char(10))
WHILE LEN(#String) > 1
BEGIN
IF CHARINDEX(CHAR(10), #String) between 1 AND 4000
BEGIN
SET #CurrentEnd = CHARINDEX(char(10), #String) -1
set #offset = 2
END
ELSE
BEGIN
SET #CurrentEnd = 4000
set #offset = 1
END
PRINT SUBSTRING(#String, 1, #CurrentEnd)
set #string = SUBSTRING(#String, #CurrentEnd+#offset, LEN(#String))
END /*End While loop*/
Taken from http://ask.sqlservercentral.com/questions/3102/any-way-around-the-print-limit-of-nvarcharmax-in-s.html
You could do a WHILE loop based on the count on your script length divided by 8000.
EG:
DECLARE #Counter INT
SET #Counter = 0
DECLARE #TotalPrints INT
SET #TotalPrints = (LEN(#script) / 8000) + 1
WHILE #Counter < #TotalPrints
BEGIN
-- Do your printing...
SET #Counter = #Counter + 1
END
Came across this question and wanted something more simple... Try the following:
SELECT [processing-instruction(x)]=#Script FOR XML PATH(''),TYPE
I just created a SP out of Ben's great answer:
/*
---------------------------------------------------------------------------------
PURPOSE : Print a string without the limitation of 4000 or 8000 characters.
https://stackoverflow.com/questions/7850477/how-to-print-varcharmax-using-print-statement
USAGE :
DECLARE #Result NVARCHAR(MAX)
SET #Result = 'TEST'
EXEC [dbo].[Print_Unlimited] #Result
---------------------------------------------------------------------------------
*/
ALTER PROCEDURE [dbo].[Print_Unlimited]
#String NVARCHAR(MAX)
AS
BEGIN
BEGIN TRY
---------------------------------------------------------------------------------
DECLARE #CurrentEnd BIGINT; /* track the length of the next substring */
DECLARE #Offset TINYINT; /* tracks the amount of offset needed */
SET #String = replace(replace(#String, CHAR(13) + CHAR(10), CHAR(10)), CHAR(13), CHAR(10))
WHILE LEN(#String) > 1
BEGIN
IF CHARINDEX(CHAR(10), #String) BETWEEN 1 AND 4000
BEGIN
SET #CurrentEnd = CHARINDEX(CHAR(10), #String) -1
SET #Offset = 2
END
ELSE
BEGIN
SET #CurrentEnd = 4000
SET #Offset = 1
END
PRINT SUBSTRING(#String, 1, #CurrentEnd)
SET #String = SUBSTRING(#String, #CurrentEnd + #Offset, LEN(#String))
END /*End While loop*/
---------------------------------------------------------------------------------
END TRY
BEGIN CATCH
DECLARE #ErrorMessage VARCHAR(4000)
SELECT #ErrorMessage = ERROR_MESSAGE()
RAISERROR(#ErrorMessage,16,1)
END CATCH
END
This proc correctly prints out VARCHAR(MAX) parameter considering wrapping:
CREATE PROCEDURE [dbo].[Print]
#sql varchar(max)
AS
BEGIN
declare
#n int,
#i int = 0,
#s int = 0, -- substring start posotion
#l int; -- substring length
set #n = ceiling(len(#sql) / 8000.0);
while #i < #n
begin
set #l = 8000 - charindex(char(13), reverse(substring(#sql, #s, 8000)));
print substring(#sql, #s, #l);
set #i = #i + 1;
set #s = #s + #l + 2; -- accumulation + CR/LF
end
return 0
END
I was looking to use the print statement to debug some dynamic sql as I imagin most of you are using print for simliar reasons.
I tried a few of the solutions listed and found that Kelsey's solution works with minor tweeks (#sql is my #script) n.b. LENGTH isn't a valid function:
--http://stackoverflow.com/questions/7850477/how-to-print-varcharmax-using-print-statement
--Kelsey
DECLARE #Counter INT
SET #Counter = 0
DECLARE #TotalPrints INT
SET #TotalPrints = (LEN(#sql) / 4000) + 1
WHILE #Counter < #TotalPrints
BEGIN
PRINT SUBSTRING(#sql, #Counter * 4000, 4000)
SET #Counter = #Counter + 1
END
PRINT LEN(#sql)
This code does as commented add a new line into the output, but for debugging this isn't a problem for me.
Ben B's solution is perfect and is the most elegent, although for debugging is a lot of lines of code so I choose to use my slight modification of Kelsey's. It might be worth creating a system like stored procedure in msdb for Ben B's code which could be reused and called in one line?
Alfoks' code doesn't work unfortunately because that would have been easier.
You can use this
declare #i int = 1
while Exists(Select(Substring(#Script,#i,4000))) and (#i < LEN(#Script))
begin
print Substring(#Script,#i,4000)
set #i = #i+4000
end
Or simply:
PRINT SUBSTRING(#SQL_InsertQuery, 1, 8000)
PRINT SUBSTRING(#SQL_InsertQuery, 8001, 16000)
There is great function called PrintMax written by Bennett Dill.
Here is slightly modified version that uses temp stored procedure to avoid "schema polution"(idea from https://github.com/Toolien/sp_GenMerge/blob/master/sp_GenMerge.sql)
EXEC (N'IF EXISTS (SELECT * FROM tempdb.sys.objects
WHERE object_id = OBJECT_ID(N''tempdb..#PrintMax'')
AND type in (N''P'', N''PC''))
DROP PROCEDURE #PrintMax;');
EXEC (N'CREATE PROCEDURE #PrintMax(#iInput NVARCHAR(MAX))
AS
BEGIN
IF #iInput IS NULL
RETURN;
DECLARE #ReversedData NVARCHAR(MAX)
, #LineBreakIndex INT
, #SearchLength INT;
SET #SearchLength = 4000;
WHILE LEN(#iInput) > #SearchLength
BEGIN
SET #ReversedData = LEFT(#iInput COLLATE DATABASE_DEFAULT, #SearchLength);
SET #ReversedData = REVERSE(#ReversedData COLLATE DATABASE_DEFAULT);
SET #LineBreakIndex = CHARINDEX(CHAR(10) + CHAR(13),
#ReversedData COLLATE DATABASE_DEFAULT);
PRINT LEFT(#iInput, #SearchLength - #LineBreakIndex + 1);
SET #iInput = RIGHT(#iInput, LEN(#iInput) - #SearchLength
+ #LineBreakIndex - 1);
END;
IF LEN(#iInput) > 0
PRINT #iInput;
END;');
DBFiddle Demo
EDIT:
Using CREATE OR ALTER we could avoid two EXEC calls:
EXEC (N'CREATE OR ALTER PROCEDURE #PrintMax(#iInput NVARCHAR(MAX))
AS
BEGIN
IF #iInput IS NULL
RETURN;
DECLARE #ReversedData NVARCHAR(MAX)
, #LineBreakIndex INT
, #SearchLength INT;
SET #SearchLength = 4000;
WHILE LEN(#iInput) > #SearchLength
BEGIN
SET #ReversedData = LEFT(#iInput COLLATE DATABASE_DEFAULT, #SearchLength);
SET #ReversedData = REVERSE(#ReversedData COLLATE DATABASE_DEFAULT);
SET #LineBreakIndex = CHARINDEX(CHAR(10) + CHAR(13), #ReversedData COLLATE DATABASE_DEFAULT);
PRINT LEFT(#iInput, #SearchLength - #LineBreakIndex + 1);
SET #iInput = RIGHT(#iInput, LEN(#iInput) - #SearchLength + #LineBreakIndex - 1);
END;
IF LEN(#iInput) > 0
PRINT #iInput;
END;');
db<>fiddle Demo
create procedure dbo.PrintMax #text nvarchar(max)
as
begin
declare #i int, #newline nchar(2), #print varchar(max);
set #newline = nchar(13) + nchar(10);
select #i = charindex(#newline, #text);
while (#i > 0)
begin
select #print = substring(#text,0,#i);
while (len(#print) > 8000)
begin
print substring(#print,0,8000);
select #print = substring(#print,8000,len(#print));
end
print #print;
select #text = substring(#text,#i+2,len(#text));
select #i = charindex(#newline, #text);
end
print #text;
end
Uses Line Feeds and spaces as a good break point:
declare #sqlAll as nvarchar(max)
set #sqlAll = '-- Insert all your sql here'
print '#sqlAll - truncated over 4000'
print #sqlAll
print ' '
print ' '
print ' '
print '#sqlAll - split into chunks'
declare #i int = 1, #nextspace int = 0, #newline nchar(2)
set #newline = nchar(13) + nchar(10)
while Exists(Select(Substring(#sqlAll,#i,3000))) and (#i < LEN(#sqlAll))
begin
while Substring(#sqlAll,#i+3000+#nextspace,1) <> ' ' and Substring(#sqlAll,#i+3000+#nextspace,1) <> #newline
BEGIN
set #nextspace = #nextspace + 1
end
print Substring(#sqlAll,#i,3000+#nextspace)
set #i = #i+3000+#nextspace
set #nextspace = 0
end
print ' '
print ' '
print ' '
My PrintMax version for prevent bad line breaks on output:
CREATE PROCEDURE [dbo].[PrintMax](#iInput NVARCHAR(MAX))
AS
BEGIN
Declare #i int;
Declare #NEWLINE char(1) = CHAR(13) + CHAR(10);
While LEN(#iInput)>0 BEGIN
Set #i = CHARINDEX(#NEWLINE, #iInput)
if #i>8000 OR #i=0 Set #i=8000
Print SUBSTRING(#iInput, 0, #i)
Set #iInput = SUBSTRING(#iInput, #i+1, LEN(#iInput))
END
END
Here's another version. This one extracts each substring to print from the main string instead of taking reducing the main string by 4000 on each loop (which might create a lot of very long strings under the hood - not sure).
CREATE PROCEDURE [Internal].[LongPrint]
#msg nvarchar(max)
AS
BEGIN
-- SET NOCOUNT ON reduces network overhead
SET NOCOUNT ON;
DECLARE #MsgLen int;
DECLARE #CurrLineStartIdx int = 1;
DECLARE #CurrLineEndIdx int;
DECLARE #CurrLineLen int;
DECLARE #SkipCount int;
-- Normalise line end characters.
SET #msg = REPLACE(#msg, char(13) + char(10), char(10));
SET #msg = REPLACE(#msg, char(13), char(10));
-- Store length of the normalised string.
SET #MsgLen = LEN(#msg);
-- Special case: Empty string.
IF #MsgLen = 0
BEGIN
PRINT '';
RETURN;
END
-- Find the end of next substring to print.
SET #CurrLineEndIdx = CHARINDEX(CHAR(10), #msg);
IF #CurrLineEndIdx BETWEEN 1 AND 4000
BEGIN
SET #CurrLineEndIdx = #CurrLineEndIdx - 1
SET #SkipCount = 2;
END
ELSE
BEGIN
SET #CurrLineEndIdx = 4000;
SET #SkipCount = 1;
END
-- Loop: Print current substring, identify next substring (a do-while pattern is preferable but TSQL doesn't have one).
WHILE #CurrLineStartIdx < #MsgLen
BEGIN
-- Print substring.
PRINT SUBSTRING(#msg, #CurrLineStartIdx, (#CurrLineEndIdx - #CurrLineStartIdx)+1);
-- Move to start of next substring.
SET #CurrLineStartIdx = #CurrLineEndIdx + #SkipCount;
-- Find the end of next substring to print.
SET #CurrLineEndIdx = CHARINDEX(CHAR(10), #msg, #CurrLineStartIdx);
SET #CurrLineLen = #CurrLineEndIdx - #CurrLineStartIdx;
-- Find bounds of next substring to print.
IF #CurrLineLen BETWEEN 1 AND 4000
BEGIN
SET #CurrLineEndIdx = #CurrLineEndIdx - 1
SET #SkipCount = 2;
END
ELSE
BEGIN
SET #CurrLineEndIdx = #CurrLineStartIdx + 4000;
SET #SkipCount = 1;
END
END
END
This should work properly this is just an improvement of previous answers.
DECLARE #Counter INT
DECLARE #Counter1 INT
SET #Counter = 0
SET #Counter1 = 0
DECLARE #TotalPrints INT
SET #TotalPrints = (LEN(#QUERY) / 4000) + 1
print #TotalPrints
WHILE #Counter < #TotalPrints
BEGIN
-- Do your printing...
print(substring(#query,#COUNTER1,#COUNTER1+4000))
set #COUNTER1 = #Counter1+4000
SET #Counter = #Counter + 1
END
If the source code will not have issues with LF to be replaced by CRLF, No debugging is required by following simple codes outputs.
--http://stackoverflow.com/questions/7850477/how-to-print-varcharmax-using-print-statement
--Bill Bai
SET #SQL=replace(#SQL,char(10),char(13)+char(10))
SET #SQL=replace(#SQL,char(13)+char(13)+char(10),char(13)+char(10) )
DECLARE #Position int
WHILE Len(#SQL)>0
BEGIN
SET #Position=charindex(char(10),#SQL)
PRINT left(#SQL,#Position-2)
SET #SQL=substring(#SQL,#Position+1,len(#SQL))
end;
If someone interested I've ended up as generating a text file with powershell, executing scalar code:
$dbconn = "Data Source=sqlserver;" + "Initial Catalog=DatabaseName;" + "User Id=sa;Password=pass;"
$conn = New-Object System.Data.SqlClient.SqlConnection($dbconn)
$conn.Open()
$cmd = New-Object System.Data.SqlClient.SqlCommand("
set nocount on
DECLARE #sql nvarchar(max) = ''
SELECT
#sql += CHAR(13) + CHAR(10) + md.definition + CHAR(13) + CHAR(10) + 'GO'
FROM sys.objects AS obj
join sys.sql_modules AS md on md.object_id = obj.object_id
join sys.schemas AS sch on sch.schema_id = obj.schema_id
where obj.type = 'TR'
select #sql
", $conn)
$data = [string]$cmd.ExecuteScalar()
$conn.Close()
$data | Out-File -FilePath "C:\Users\Alexandru\Desktop\bigstring.txt"
This script it's for getting a big string with all the triggers from the DB.
Related
Query generate too many results, output didnt display them all [duplicate]
I have a code which is: DECLARE #Script VARCHAR(MAX) SELECT #Script = definition FROM manged.sys.all_sql_modules sq where sq.object_id = (SELECT object_id from managed.sys.objects Where type = 'P' and Name = 'usp_gen_data') Declare #Pos int SELECT #pos=CHARINDEX(CHAR(13)+CHAR(10),#script,7500) PRINT SUBSTRING(#Script,1,#Pos) PRINT SUBSTRING(#script,#pos,8000) The length of the Script is around 10,000 Characters and Since I am using print Statement which can hold only max of 8000. So I am using two print statements. The problem is when I have a script which is of say 18000 characters then I used to use 3 print statements. So Is there a way that I could set the number of print statements depending on the length of the script?
I know it's an old question, but what I did is not mentioned here. For me the following worked (for up to 16k chars) DECLARE #info NVARCHAR(MAX) --SET #info to something big PRINT CAST(#info AS NTEXT) If you have more than 16k chars you can combine with #Yovav's answer like this (64k should be enough for anyone ;) print cast( substring(#info, 1, 16000) as ntext ) print cast( substring(#info, 16001, 32000) as ntext ) print cast( substring(#info, 32001, 48000) as ntext ) print cast( substring(#info, 48001, 64000) as ntext )
The following workaround does not use the PRINT statement. It works well in combination with the SQL Server Management Studio. SELECT CAST('<root><![CDATA[' + #MyLongString + ']]></root>' AS XML) You can click on the returned XML to expand it in the built-in XML viewer. There is a pretty generous client side limit on the displayed size. Go to Tools/Options/Query Results/SQL Server/Results to Grid/XML data to adjust it if needed.
Here is how this should be done: DECLARE #String NVARCHAR(MAX); DECLARE #CurrentEnd BIGINT; /* track the length of the next substring */ DECLARE #offset tinyint; /*tracks the amount of offset needed */ set #string = replace( replace(#string, char(13) + char(10), char(10)) , char(13), char(10)) WHILE LEN(#String) > 1 BEGIN IF CHARINDEX(CHAR(10), #String) between 1 AND 4000 BEGIN SET #CurrentEnd = CHARINDEX(char(10), #String) -1 set #offset = 2 END ELSE BEGIN SET #CurrentEnd = 4000 set #offset = 1 END PRINT SUBSTRING(#String, 1, #CurrentEnd) set #string = SUBSTRING(#String, #CurrentEnd+#offset, LEN(#String)) END /*End While loop*/ Taken from http://ask.sqlservercentral.com/questions/3102/any-way-around-the-print-limit-of-nvarcharmax-in-s.html
You could do a WHILE loop based on the count on your script length divided by 8000. EG: DECLARE #Counter INT SET #Counter = 0 DECLARE #TotalPrints INT SET #TotalPrints = (LEN(#script) / 8000) + 1 WHILE #Counter < #TotalPrints BEGIN -- Do your printing... SET #Counter = #Counter + 1 END
Came across this question and wanted something more simple... Try the following: SELECT [processing-instruction(x)]=#Script FOR XML PATH(''),TYPE
I just created a SP out of Ben's great answer: /* --------------------------------------------------------------------------------- PURPOSE : Print a string without the limitation of 4000 or 8000 characters. https://stackoverflow.com/questions/7850477/how-to-print-varcharmax-using-print-statement USAGE : DECLARE #Result NVARCHAR(MAX) SET #Result = 'TEST' EXEC [dbo].[Print_Unlimited] #Result --------------------------------------------------------------------------------- */ ALTER PROCEDURE [dbo].[Print_Unlimited] #String NVARCHAR(MAX) AS BEGIN BEGIN TRY --------------------------------------------------------------------------------- DECLARE #CurrentEnd BIGINT; /* track the length of the next substring */ DECLARE #Offset TINYINT; /* tracks the amount of offset needed */ SET #String = replace(replace(#String, CHAR(13) + CHAR(10), CHAR(10)), CHAR(13), CHAR(10)) WHILE LEN(#String) > 1 BEGIN IF CHARINDEX(CHAR(10), #String) BETWEEN 1 AND 4000 BEGIN SET #CurrentEnd = CHARINDEX(CHAR(10), #String) -1 SET #Offset = 2 END ELSE BEGIN SET #CurrentEnd = 4000 SET #Offset = 1 END PRINT SUBSTRING(#String, 1, #CurrentEnd) SET #String = SUBSTRING(#String, #CurrentEnd + #Offset, LEN(#String)) END /*End While loop*/ --------------------------------------------------------------------------------- END TRY BEGIN CATCH DECLARE #ErrorMessage VARCHAR(4000) SELECT #ErrorMessage = ERROR_MESSAGE() RAISERROR(#ErrorMessage,16,1) END CATCH END
This proc correctly prints out VARCHAR(MAX) parameter considering wrapping: CREATE PROCEDURE [dbo].[Print] #sql varchar(max) AS BEGIN declare #n int, #i int = 0, #s int = 0, -- substring start posotion #l int; -- substring length set #n = ceiling(len(#sql) / 8000.0); while #i < #n begin set #l = 8000 - charindex(char(13), reverse(substring(#sql, #s, 8000))); print substring(#sql, #s, #l); set #i = #i + 1; set #s = #s + #l + 2; -- accumulation + CR/LF end return 0 END
I was looking to use the print statement to debug some dynamic sql as I imagin most of you are using print for simliar reasons. I tried a few of the solutions listed and found that Kelsey's solution works with minor tweeks (#sql is my #script) n.b. LENGTH isn't a valid function: --http://stackoverflow.com/questions/7850477/how-to-print-varcharmax-using-print-statement --Kelsey DECLARE #Counter INT SET #Counter = 0 DECLARE #TotalPrints INT SET #TotalPrints = (LEN(#sql) / 4000) + 1 WHILE #Counter < #TotalPrints BEGIN PRINT SUBSTRING(#sql, #Counter * 4000, 4000) SET #Counter = #Counter + 1 END PRINT LEN(#sql) This code does as commented add a new line into the output, but for debugging this isn't a problem for me. Ben B's solution is perfect and is the most elegent, although for debugging is a lot of lines of code so I choose to use my slight modification of Kelsey's. It might be worth creating a system like stored procedure in msdb for Ben B's code which could be reused and called in one line? Alfoks' code doesn't work unfortunately because that would have been easier.
You can use this declare #i int = 1 while Exists(Select(Substring(#Script,#i,4000))) and (#i < LEN(#Script)) begin print Substring(#Script,#i,4000) set #i = #i+4000 end
Or simply: PRINT SUBSTRING(#SQL_InsertQuery, 1, 8000) PRINT SUBSTRING(#SQL_InsertQuery, 8001, 16000)
There is great function called PrintMax written by Bennett Dill. Here is slightly modified version that uses temp stored procedure to avoid "schema polution"(idea from https://github.com/Toolien/sp_GenMerge/blob/master/sp_GenMerge.sql) EXEC (N'IF EXISTS (SELECT * FROM tempdb.sys.objects WHERE object_id = OBJECT_ID(N''tempdb..#PrintMax'') AND type in (N''P'', N''PC'')) DROP PROCEDURE #PrintMax;'); EXEC (N'CREATE PROCEDURE #PrintMax(#iInput NVARCHAR(MAX)) AS BEGIN IF #iInput IS NULL RETURN; DECLARE #ReversedData NVARCHAR(MAX) , #LineBreakIndex INT , #SearchLength INT; SET #SearchLength = 4000; WHILE LEN(#iInput) > #SearchLength BEGIN SET #ReversedData = LEFT(#iInput COLLATE DATABASE_DEFAULT, #SearchLength); SET #ReversedData = REVERSE(#ReversedData COLLATE DATABASE_DEFAULT); SET #LineBreakIndex = CHARINDEX(CHAR(10) + CHAR(13), #ReversedData COLLATE DATABASE_DEFAULT); PRINT LEFT(#iInput, #SearchLength - #LineBreakIndex + 1); SET #iInput = RIGHT(#iInput, LEN(#iInput) - #SearchLength + #LineBreakIndex - 1); END; IF LEN(#iInput) > 0 PRINT #iInput; END;'); DBFiddle Demo EDIT: Using CREATE OR ALTER we could avoid two EXEC calls: EXEC (N'CREATE OR ALTER PROCEDURE #PrintMax(#iInput NVARCHAR(MAX)) AS BEGIN IF #iInput IS NULL RETURN; DECLARE #ReversedData NVARCHAR(MAX) , #LineBreakIndex INT , #SearchLength INT; SET #SearchLength = 4000; WHILE LEN(#iInput) > #SearchLength BEGIN SET #ReversedData = LEFT(#iInput COLLATE DATABASE_DEFAULT, #SearchLength); SET #ReversedData = REVERSE(#ReversedData COLLATE DATABASE_DEFAULT); SET #LineBreakIndex = CHARINDEX(CHAR(10) + CHAR(13), #ReversedData COLLATE DATABASE_DEFAULT); PRINT LEFT(#iInput, #SearchLength - #LineBreakIndex + 1); SET #iInput = RIGHT(#iInput, LEN(#iInput) - #SearchLength + #LineBreakIndex - 1); END; IF LEN(#iInput) > 0 PRINT #iInput; END;'); db<>fiddle Demo
create procedure dbo.PrintMax #text nvarchar(max) as begin declare #i int, #newline nchar(2), #print varchar(max); set #newline = nchar(13) + nchar(10); select #i = charindex(#newline, #text); while (#i > 0) begin select #print = substring(#text,0,#i); while (len(#print) > 8000) begin print substring(#print,0,8000); select #print = substring(#print,8000,len(#print)); end print #print; select #text = substring(#text,#i+2,len(#text)); select #i = charindex(#newline, #text); end print #text; end
Uses Line Feeds and spaces as a good break point: declare #sqlAll as nvarchar(max) set #sqlAll = '-- Insert all your sql here' print '#sqlAll - truncated over 4000' print #sqlAll print ' ' print ' ' print ' ' print '#sqlAll - split into chunks' declare #i int = 1, #nextspace int = 0, #newline nchar(2) set #newline = nchar(13) + nchar(10) while Exists(Select(Substring(#sqlAll,#i,3000))) and (#i < LEN(#sqlAll)) begin while Substring(#sqlAll,#i+3000+#nextspace,1) <> ' ' and Substring(#sqlAll,#i+3000+#nextspace,1) <> #newline BEGIN set #nextspace = #nextspace + 1 end print Substring(#sqlAll,#i,3000+#nextspace) set #i = #i+3000+#nextspace set #nextspace = 0 end print ' ' print ' ' print ' '
My PrintMax version for prevent bad line breaks on output: CREATE PROCEDURE [dbo].[PrintMax](#iInput NVARCHAR(MAX)) AS BEGIN Declare #i int; Declare #NEWLINE char(1) = CHAR(13) + CHAR(10); While LEN(#iInput)>0 BEGIN Set #i = CHARINDEX(#NEWLINE, #iInput) if #i>8000 OR #i=0 Set #i=8000 Print SUBSTRING(#iInput, 0, #i) Set #iInput = SUBSTRING(#iInput, #i+1, LEN(#iInput)) END END
Here's another version. This one extracts each substring to print from the main string instead of taking reducing the main string by 4000 on each loop (which might create a lot of very long strings under the hood - not sure). CREATE PROCEDURE [Internal].[LongPrint] #msg nvarchar(max) AS BEGIN -- SET NOCOUNT ON reduces network overhead SET NOCOUNT ON; DECLARE #MsgLen int; DECLARE #CurrLineStartIdx int = 1; DECLARE #CurrLineEndIdx int; DECLARE #CurrLineLen int; DECLARE #SkipCount int; -- Normalise line end characters. SET #msg = REPLACE(#msg, char(13) + char(10), char(10)); SET #msg = REPLACE(#msg, char(13), char(10)); -- Store length of the normalised string. SET #MsgLen = LEN(#msg); -- Special case: Empty string. IF #MsgLen = 0 BEGIN PRINT ''; RETURN; END -- Find the end of next substring to print. SET #CurrLineEndIdx = CHARINDEX(CHAR(10), #msg); IF #CurrLineEndIdx BETWEEN 1 AND 4000 BEGIN SET #CurrLineEndIdx = #CurrLineEndIdx - 1 SET #SkipCount = 2; END ELSE BEGIN SET #CurrLineEndIdx = 4000; SET #SkipCount = 1; END -- Loop: Print current substring, identify next substring (a do-while pattern is preferable but TSQL doesn't have one). WHILE #CurrLineStartIdx < #MsgLen BEGIN -- Print substring. PRINT SUBSTRING(#msg, #CurrLineStartIdx, (#CurrLineEndIdx - #CurrLineStartIdx)+1); -- Move to start of next substring. SET #CurrLineStartIdx = #CurrLineEndIdx + #SkipCount; -- Find the end of next substring to print. SET #CurrLineEndIdx = CHARINDEX(CHAR(10), #msg, #CurrLineStartIdx); SET #CurrLineLen = #CurrLineEndIdx - #CurrLineStartIdx; -- Find bounds of next substring to print. IF #CurrLineLen BETWEEN 1 AND 4000 BEGIN SET #CurrLineEndIdx = #CurrLineEndIdx - 1 SET #SkipCount = 2; END ELSE BEGIN SET #CurrLineEndIdx = #CurrLineStartIdx + 4000; SET #SkipCount = 1; END END END
This should work properly this is just an improvement of previous answers. DECLARE #Counter INT DECLARE #Counter1 INT SET #Counter = 0 SET #Counter1 = 0 DECLARE #TotalPrints INT SET #TotalPrints = (LEN(#QUERY) / 4000) + 1 print #TotalPrints WHILE #Counter < #TotalPrints BEGIN -- Do your printing... print(substring(#query,#COUNTER1,#COUNTER1+4000)) set #COUNTER1 = #Counter1+4000 SET #Counter = #Counter + 1 END
If the source code will not have issues with LF to be replaced by CRLF, No debugging is required by following simple codes outputs. --http://stackoverflow.com/questions/7850477/how-to-print-varcharmax-using-print-statement --Bill Bai SET #SQL=replace(#SQL,char(10),char(13)+char(10)) SET #SQL=replace(#SQL,char(13)+char(13)+char(10),char(13)+char(10) ) DECLARE #Position int WHILE Len(#SQL)>0 BEGIN SET #Position=charindex(char(10),#SQL) PRINT left(#SQL,#Position-2) SET #SQL=substring(#SQL,#Position+1,len(#SQL)) end;
If someone interested I've ended up as generating a text file with powershell, executing scalar code: $dbconn = "Data Source=sqlserver;" + "Initial Catalog=DatabaseName;" + "User Id=sa;Password=pass;" $conn = New-Object System.Data.SqlClient.SqlConnection($dbconn) $conn.Open() $cmd = New-Object System.Data.SqlClient.SqlCommand(" set nocount on DECLARE #sql nvarchar(max) = '' SELECT #sql += CHAR(13) + CHAR(10) + md.definition + CHAR(13) + CHAR(10) + 'GO' FROM sys.objects AS obj join sys.sql_modules AS md on md.object_id = obj.object_id join sys.schemas AS sch on sch.schema_id = obj.schema_id where obj.type = 'TR' select #sql ", $conn) $data = [string]$cmd.ExecuteScalar() $conn.Close() $data | Out-File -FilePath "C:\Users\Alexandru\Desktop\bigstring.txt" This script it's for getting a big string with all the triggers from the DB.
MSG 537 :Invalid length parameter passed to the LEFT or SUBSTRING function
when i create a function in sql server 2012 , in this function CREATE FUNCTION howords (#str nvarchar(50), #word nvarchar(50)) RETURNS int AS BEGIN DECLARE #tempstr nvarchar(max) DECLARE #space int DECLARE #count int DECLARE #size int SET #count = 0 WHILE (LEN(#str) >= 0) BEGIN SET #space = CHARINDEX(' ', #str, 1) SET #tempstr = LEFT(#str, (#space - 1)) IF (#tempstr = #word) SET #count = #count + 1 SET #size = LEN(#tempstr) IF (((LEN(#str)) - #space) > 0) SET #str = SUBSTRING(#str, #space + 1, ((LEN(#str)) - #space)) IF (((LEN(#str)) - #space) <= 0) BEGIN IF (#str = #word) SET #count = 0 WHILE (LEN(#str) > 0) BEGIN SET #space = CHARINDEX(' ', #str + 1) SET #tempstr = LEFT(#str, (#space - 1)) IF (#tempstr = #word) SET #count = #count + 1 SET #size = LEN(#tempstr) IF (((LEN(#str)) - #space) > 0) SET #str = SUBSTRING(#str, #space + 1, ((LEN(#str)) - #space)) IF (((LEN(#str)) - #space) <= 0) BEGIN IF (#str = #word) SET #count = #count + 1 BREAK END END END END RETURN #count END when i exec this query select dbo.howords ('hello my hello','hello' ) i want to give me count (2) but it give me an error MSG 537 :Invalid length parameter passed to the LEFT or SUBSTRING function. any help?
Try this DECLARE #tosearch VARCHAR(MAX)='Hello' DECLARE #string VARCHAR(MAX)='hello my hello' SELECT (DATALENGTH(#string)-DATALENGTH(REPLACE(#string,#tosearch,'')))/DATALENGTH(#tosearch) AS OccurrenceCount
SQL - Replace non numeric characters in string
I'm writing a function that takes a string and has to replace any non-numeric character with two numeric characters taken from a table. This is the code I use to explore the string and find the non-numeric characters: SET #string = '1a2b3c4d' SET #wcount= 0 SET #index = 1 SET #len= LEN(#string) WHILE #index<= #len BEGIN SET #char = SUBSTRING(#string, #index, 1) IF #char LIKE '%[a-z]%' PRINT 'char ' + CONVERT(varchar(10), #index) ELSE PRINT #char SET #index= #index+ 1 END The output is the following 1 char 2 2 char 4 3 char 6 4 char 8 Now, when I find a non-numeric character I have to replace it with two numeric chars taken from a table by a select. E.g. SELECT #temp = REPLACEMENT FROM Conversion_Tab WHERE EXPR = #char In conclusion, if I have the following string '1a2a3a4a' and the replacement for 'a' is '88' the resulting string should be '188288388488' Thanks in advance for any help. Bye!
Try this DECLARE #string VARCHAR(100) DECLARE #outstring VARCHAR(100) DECLARE #wcount INT DECLARE #temp INT DECLARE #index INT DECLARE #len INT DECLARE #char CHAR SET #string = '1a2a3a4a' SET #wcount= 0 SET #index = 1 SET #len= LEN(#string) SET #outstring = '' WHILE #index<= #len BEGIN SET #char = SUBSTRING(#string, #index, 1) IF #char LIKE '%[a-z]%' BEGIN SELECT #temp = REPLACEMENT FROM #Conversion_Tab WHERE EXPR = #char SET #outstring = #outstring + CONVERT(VARCHAR(10),#temp) END ELSE BEGIN SET #outstring = #outstring + #char END SET #index= #index+ 1 END SELECT #outstring SQL FIDDLE DEMO
looks like you need isnumeric(). so if not isnumeric(char) replace it with your lookup value. taken from the other answer but produces the same result DECLARE #string VARCHAR(100) DECLARE #outstring VARCHAR(100) DECLARE #wcount INT DECLARE #temp INT DECLARE #index INT DECLARE #len INT DECLARE #char CHAR SET #string = '1a2a3a4a' SET #wcount= 0 SET #index = 1 SET #len= LEN(#string) SET #outstring = '' WHILE #index<= #len BEGIN SET #char = SUBSTRING(#string, #index, 1) IF ISNUMERIC(#char) = 0 BEGIN SELECT #temp = REPLACEMENT FROM #Conversion_Tab WHERE EXPR = #char SET #outstring = #outstring + CONVERT(VARCHAR(10),#temp) END ELSE BEGIN SET #outstring = #outstring + #char END SET #index= #index+ 1 END
Converting String List into Int List in SQL
I have a nvarchar(MAX) in my stored procedure which contains the list of int values, I did it like this as it is not possible to pass int list to my stored procedure, but, now I am getting problem as my datatype is int and I want to compare the list of string. Is there a way around by which I can do the same? ---myquerry----where status in (#statuslist) but the statuslist contains now string values not int, so how to convert them into INT? UPDate: USE [Database] GO SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO ALTER PROCEDURE [dbo].[SP] ( #FromDate datetime = 0, #ToDate datetime = 0, #ID int=0, #List nvarchar(MAX) //This is the List which has string ids// ) AS SET FMTONLY OFF; DECLARE #sql nvarchar(MAX), #paramlist nvarchar(MAX) SET #sql = 'SELECT ------ and Code in(#xList) and -------------' SELECT #paramlist = '#xFromDate datetime,#xToDate datetime,#xId int,#xList nvarchar(MAX)' EXEC sp_executesql #sql, #paramlist, #xFromDate = #FromDate ,#xToDate=#ToDate,#xId=#ID,#xList=#List PRINT #sql So when I implement that function that splits then I am not able to specify the charcter or delimiter as it is not accepting it as (#List,','). or (','+#List+',').
It is possible to send an int list to your stored procedure using XML parameters. This way you don't have to tackle this problem anymore and it is a better and more clean solution. have a look at this question: Passing an array of parameters to a stored procedure or check this code project: http://www.codeproject.com/Articles/20847/Passing-Arrays-in-SQL-Parameters-using-XML-Data-Ty However if you insist on doing it your way you could use this function: CREATE FUNCTION [dbo].[fnStringList2Table] ( #List varchar(MAX) ) RETURNS #ParsedList table ( item int ) AS BEGIN DECLARE #item varchar(800), #Pos int SET #List = LTRIM(RTRIM(#List))+ ',' SET #Pos = CHARINDEX(',', #List, 1) WHILE #Pos > 0 BEGIN SET #item = LTRIM(RTRIM(LEFT(#List, #Pos - 1))) IF #item <> '' BEGIN INSERT INTO #ParsedList (item) VALUES (CAST(#item AS int)) END SET #List = RIGHT(#List, LEN(#List) - #Pos) SET #Pos = CHARINDEX(',', #List, 1) END RETURN END Call it like this: SELECT * FROM Table WHERE status IN (SELECT * from fnStringList2Table(#statuslist))
You can work with string list too. I always do. declare #statuslist nvarchar(max) set #statuslist = '1, 2, 3, 4' declare #sql nvarchar(max) set #sql = 'select * from table where Status in (' + #statuslist + ')' Execute(#sql)
You can do this by using sql function which will return you an integer array.. It would be great if you pass #Delimiter separated string to your stored procedure which could be processed properly afterwards. Write one function to split the data as following CREATE FUNCTION [dbo].[SplitValues] (#StringArray NVARCHAR(MAX), #Delimiter NVARCHAR(10)) RETURNS #ResultedValues table ( ResultValue INT ) AS BEGIN DECLARE #Tokens TABLE(Token nvarchar) DECLARE #String nvarchar WHILE (CHARINDEX(#Delimiter,#StringArray)>0) BEGIN INSERT INTO #Tokens (Token) VALUES (LTRIM(RTRIM(SUBSTRING(#StringArray,1,CHARINDEX(#Delimiter,#StringArray)-1)))) SET #String = SUBSTRING(#StringArray, CHARINDEX(#Delimiter,#StringArray)+LEN(#Delimiter),LEN(#StringArray)) END INSERT INTO #ResultedValues (ResultValue ) VALUES ( CAST(LTRIM(RTRIM(#String)) AS INT)) RETURN END And then use it like following, i am using (,) as #Delimiter here SELECT ResultValue [YourSchema].[SplitValues](#statuslist,',')
Actually, you can send the list of int values to your procedure by creating a User Defined Table Type. However, this implies more work in order to populate the table parameter. In your case, you can use the sp_executesql stored procedure to achieve what you want like this: declare #statement nvarchar(4000) = '----your query---- where status in (' + #statusList +')' sp_executesql #statement
here is an example of how to do it and the Link for more informations ALTER FUNCTION iter_intlist_to_tbl (#list nvarchar(MAX)) RETURNS #tbl TABLE (listpos int IDENTITY(1, 1) NOT NULL, number int NOT NULL) AS BEGIN DECLARE #startpos int, #endpos int, #textpos int, #chunklen smallint, #str nvarchar(4000), #tmpstr nvarchar(4000), #leftover nvarchar(4000) SET #textpos = 1 SET #leftover = '' WHILE #textpos <= datalength(#list) / 2 BEGIN SET #chunklen = 4000 - datalength(#leftover) / 2 SET #tmpstr = ltrim(#leftover + substring(#list, #textpos, #chunklen)) SET #textpos = #textpos + #chunklen SET #startpos = 0 SET #endpos = charindex(' ' COLLATE Slovenian_BIN2, #tmpstr) WHILE #endpos > 0 BEGIN SET #str = substring(#tmpstr, #startpos + 1, #endpos - #startpos - 1) IF #str <> '' INSERT #tbl (number) VALUES(convert(int, #str)) SET #startpos = #endpos SET #endpos = charindex(' ' COLLATE Slovenian_BIN2, #tmpstr, #startpos + 1) END SET #leftover = right(#tmpstr, datalength(#tmpstr) / 2 - #startpos) END IF ltrim(rtrim(#leftover)) <> '' INSERT #tbl (number) VALUES(convert(int, #leftover)) RETURN END -- ############################ Example ############################ --CREATE PROCEDURE get_product_names_iter #ids varchar(50) AS --SELECT P.ProductName, P.ProductID --FROM Northwind..Products P --JOIN iter_intlist_to_tbl(#ids) i ON P.ProductID = i.number --go --EXEC get_product_names_iter '9 12 27 37' -- ############################ WICHTIG ############################
This works for me on an Informix DataBase: DROP FUNCTION rrhh:fnc_StringList_To_Table; CREATE FUNCTION rrhh:fnc_StringList_To_Table (pStringList varchar(250)) RETURNING INT as NUMERO; /* A esta Funcion le podes pasar una cadena CSV con una lista de numeros * Ejem: EXECUTE FUNCTION fnc_StringList_To_Table('1,2,3,4'); * y te devolvera una Tabla con dichos numeros separados uno x fila * Autor: Jhollman Chacon #Cutcsa - 2019 */ DEFINE _STRING VARCHAR(255); DEFINE _LEN INT; DEFINE _POS INT; DEFINE _START INT; DEFINE _CHAR VARCHAR(1); DEFINE _VAL INT; LET _STRING = REPLACE(pStringList, ' ', ''); LET _START = 0; LET _POS = 0; LET _LEN = LENGTH(_STRING); FOR _POS = _START TO _LEN LET _CHAR = SUBSTRING(pStringList FROM _POS FOR 1); IF _CHAR <> ',' THEN LET _VAL = _CHAR::INT; ELSE LET _VAL = NULL; END IF; IF _VAL IS NOT NULL THEN RETURN _VAL WITH RESUME; END IF; END FOR; END FUNCTION; EXECUTE FUNCTION fnc_StringList_To_Table('1,2,3,4'); SELECT * FROM TABLE (fnc_StringList_To_Table('1,2,3,4'));
Strange error in this SQL Server stored procedure
I found the following stored procedure from here, while creating this stored procedure in my database, I get this error message: (I'm running SQL Server 2008 Developer) Msg 102, Level 15, State 1, Procedure HighLightSearch, Line 23 Incorrect syntax near '+'. Here is the full Stored Procedure code: CREATE FUNCTION [dbo].[HighLightSearch](#contents NVARCHAR(MAX), #searchTerm NVARCHAR(4000), #style NVARCHAR(4000), #maxLen INT) RETURNS NVARCHAR(MAX) AS BEGIN DECLARE #c NCHAR(1) DECLARE #len INT = 0 DECLARE #l INT = 0 DECLARE #p INT = 0 DECLARE #prevPos INT = 0 DECLARE #margin INT DECLARE #term NVARCHAR(4000) DECLARE #retval NVARCHAR(MAX) = '' DECLARE #positions TABLE ( S INT, L INT ) -- find all occurances of the search term DECLARE cur1 CURSOR FOR SELECT display_term FROM sys.dm_fts_parser(N'FORMSOF(FREETEXT, "' + #searchTerm + '")', 1033, 0, 1) OPEN cur1 FETCH NEXT FROM cur1 INTO #term WHILE ##FETCH_STATUS = 0 BEGIN WHILE 1 = 1 BEGIN SET #p = CHARINDEX(#term, #contents, #p) IF #p <= 0 BREAK SET #l = LEN(#term) IF #p > 0 BEGIN SET #c = SUBSTRING(#contents, #p - 1, 1) IF #c <> ' ' AND #c <> NCHAR(9) AND #c <> NCHAR(13) AND #c <> NCHAR(10) BREAK END INSERT INTO #positions (S, L) VALUES(#p, #l) SET #p = #p + LEN(#term) END FETCH NEXT FROM cur1 INTO #term END CLOSE cur1 DEALLOCATE cur1 -- build the result string DECLARE cur2 CURSOR FOR SELECT S, MAX(L) FROM #positions GROUP BY S ORDER BY S SET #margin = LOG(#maxLen) * 5 IF #margin > #maxLen / 4 SET #margin = #maxLen / 4 SELECT #prevPos = MIN(S) - #margin FROM #positions OPEN cur2 FETCH NEXT FROM cur2 INTO #p, #l WHILE ##FETCH_STATUS = 0 AND #len < #maxLen BEGIN SET #retval = #retval + SUBSTRING(#contents, #prevPos, #p - #prevPos) SET #retval = #retval + '<span style="' + #style + '">' + SUBSTRING(#contents, #p, #l) + '</span>' SET #len = #len + #p - #prevPos + #l SET #prevPos = #p + #l FETCH NEXT FROM cur2 INTO #p, #l END CLOSE cur2 DEALLOCATE cur2 SET #margin = LOG(#maxLen) * 5 IF #margin + #len < #maxLen SET #margin = #maxLen - #len IF #margin > 0 SET #retval = #retval + SUBSTRING(#contents, #prevPos, #l) RETURN '...' + #retval + '...' END
Try doing DECLARE #var NVARCHAR(4000) SET #var=N'FORMSOF(FREETEXT, "' + #searchTerm + '")' DECLARE cur1 CURSOR FOR SELECT display_term FROM sys.dm_fts_parser(#var, 1033, 0, 1)