DECLARE #input NVARCHAR(MAX) = 'Create Procedure Test'
DECLARE #SearhString NVARCHAR(MAX) = 'Procedure'
DECLARE #index BIGINT = CHARINDEX(#keyword, #input)
1- I want to find the length of the next character starting after string Procedure
2- After that I want to check if the dbo string exists after that length to the specified location.
Suppose the first character Test starting at position 14 after string Procedure, so I have to use
DECLARE #String varchar(20) = SUBSTRING(#input, 21, 10)
Declare #StringIndex BIGINT = CHARINDEX('dbo.', #String)
Declare #FirstCharacterIndex BIGINT(This will be the index of the first character)
If #String not contains string dbo then stuff MySchema before string Test.
Please suggest.
UPDATE
I have updated my question 22 is the result of #index + #FirstCharacterIndex + 9
#index = 8
#FirstCharacterIndex = 4(3 count for spaces and 4th is for first character)
9 = No. of characters of string Procedure
try this sql below:
DECLARE #input NVARCHAR(MAX) = 'Create Procedure Test';
DECLARE #input2 NVARCHAR(MAX) = 'Create Procedure dbo Test';
DECLARE #SearchString NVARCHAR(MAX) = 'Procedure';
DECLARE #index BIGINT = CHARINDEX(#SearchString, #input);
select #input, #SearchString as searchstring, #index as Index_SearchString , #index +len(#SearchString) as Last_CharacterOfSearchString
, charIndex('Test', #input,#index +len(#SearchString)) as test_Index
, charIndex('dbo', #input,#index +len(#SearchString)) as dbo_Index
, substring(#input, 0, #index +len(#SearchString)) as str1
, substring(#input, #index +len(#SearchString), len(#input)-#index +len(#SearchString)) as str2
Declare #dbo_Index int
Declare #str1 nvarchar(max), #str2 nvarchar(max)
set #dbo_Index = charIndex('dbo', #input,#index +len(#SearchString))
--select #dbo_Index
if #dbo_Index = 0
begin
set #str1 = substring(#input, 0, #index +len(#SearchString))
set #str2 = substring(#input, #index +len(#SearchString), len(#input)-#index +len(#SearchString))
select #str1 + ' ' + 'MySchema' + #str2
end
--,charIndex('dbo',#input2, #index +len(#SearchString)) as dbo_Index
Updated:
objective: Get the "Under Fire" first character index ("U")
DECLARE #input2 NVARCHAR(MAX) = 'Create Procedure Under Fire Test';
DECLARE #SearchString NVARCHAR(MAX) = 'Procedure';
--,charIndex('dbo',#input2, #index +len(#SearchString)) as dbo_Index
select substring(#input2,17,25)
, CHARINDEX(#SearchString,#input2,0) + len(#SearchString) as LastIndexOf_Procedure
, CHARINDEX('Test',#input2,0)
,len(#input2)
,substring(#input2, CHARINDEX(#SearchString,#input2,0) + len(#SearchString), CHARINDEX('Test',#input2,0)- (CHARINDEX(#SearchString,#input2,0)+len(#SearchString)))
set #SearchString= Ltrim(rtrim(substring(#input2, CHARINDEX(#SearchString,#input2,0) + len(#SearchString), CHARINDEX('Test',#input2,0)- (CHARINDEX(#SearchString,#input2,0)+len(#SearchString)))))
select #SearchString
select
#input2
,#SearchString as SearchString
,CHARINDEX(#SearchString,#input2,0) as FirstCharacterIndex
Related
How can I output text until it is ascii in SQL?
Here is what I am trying to do:
DECLARE #input VARCHAR(20)
SET #input = 'text'
DECLARE #index INT
SET #index = 1
DECLARE #output VARCHAR(32)
SET #output = ''
WHILE CHAR(ASCII(SUBSTRING(#input, #index, 1))) LIKE '[ -~]'
BEGIN
SET #output = #output + CHAR(ASCII(SUBSTRING(#input, #index, 1)))
SET #index = #index + 1
END
SELECT #output
But in the end I am getting an empty string. Why? What am I missing here?
I am expecting the value of the #output to be 'text' in the end of the script.
UPDATE
If I update the script to the
DECLARE #input VARCHAR(20)
SET #input = 'text'
DECLARE #index INT
SET #index = 1
DECLARE #output VARCHAR(32)
SET #output = ''
WHILE CHAR(ASCII(SUBSTRING(#input, #index, 1))) LIKE '[a-b]'
BEGIN
SET #output = #output + CHAR(ASCII(SUBSTRING(#input, #index, 1)))
SET #index = #index + 1
END
SELECT #output
It will work as expected. But here I just shrinked the set from all printable ascii characters to only small letters. Why does the shrinked set [a-b] include the text characters and the extended set [ -~] does not?
A space is not a valid range delimiter so LIKE [ -~] will not work. That becomes a test for those three characters only.
You could check the ASCII code directly, rather than using LIKE:
DECLARE #input VARCHAR(20)
SET #input = 'text'
DECLARE #index INT
SET #index = 1
DECLARE #output VARCHAR(32)
SET #output = ''
WHILE ASCII(SUBSTRING(#input, #index, 1)) BETWEEN 32 AND 126
BEGIN
SET #output = #output + CHAR(ASCII(SUBSTRING(#input, #index, 1)))
SET #index = #index + 1
END
SELECT #output
demo
Side note: It's possible to get unexpected results with LIKE ranges because the comparison is collation-aware.
Answer copied from the duplicate question on Database Administrators
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
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'));
I have several occurrences of differences strings in the columns, like this example
'dsasdasdsd'+'ewewewew'+'45454545'+(avg('uuuuuuu'))
I need to split this string into several columns with the substrings that are between
aphostropes
like this:
Column 1 = dsasdasdsd
Column 2 = ewewewew
Column 3 = 45454545
Column 4 = uuuuuuu
The numbers of apperances are random, thefore the length of the original column is also not fixed (from 50 char to > 1000)
DECLARE #InStr VarChar(1000) = '''dsasdasdsd''+''ewewewew''+''45454545''+(avg(''uuuuuuu''))'''
DECLARE #intStart INT = 0
DECLARE #intEnd INT = 1
DECLARE #ColNo INT = 1
DECLARE #MyString VARCHAR(2000)
DECLARE #SelectString VARCHAR(8000) = 'SELECT '
WHILE(#intStart < LEN(#InStr) )
BEGIN
SELECT #intStart = CHARINDEX(CHAR(39), #InStr, 0) + 1
SELECT #intEnd = CHARINDEX(CHAR(39), #InStr, #intStart)
SELECT #SelectString = #SelectString + CHAR(39) + SUBSTRING(#InStr, #intStart, #intEnd - #intStart) + CHAR(39) + ' As [Column ' + CAST(#ColNo As Varchar) + '],'
SELECT #InStr = SUBSTRING(#InStr, #intEnd + 1, LEN(#InStr)-#intEnd )
SET #ColNo = #ColNo +1
END
SELECT #SelectString = LEFT(#SelectString, Len(#SelectString) -1)
EXEC (#SelectString)
I have been playing with this and this does run but unfortunately I don't have time right now to carry on with it but maybe you can improve on this?
HTH
You can try this:
create table tSqlStrings (sText nvarchar(1000))
insert tSqlStrings values('''dsasdasdsd''+''ewewewew''+''45454545''+(avg(''uuuuuuu''))')
create table tResults (
sColumn1 nvarchar(1000)
,sColumn2 nvarchar(1000)
,sColumn3 nvarchar(1000)
,sColumn4 nvarchar(1000)
)
and
DELETE tResults
DECLARE #sText nvarchar(1000) = (
SELECT
sText
FROM
tSqlStrings
)
DECLARE #lBegin int = CHARINDEX('''',#sText)
DECLARE #lEnd int = charindex('''',
substring(#sText,
CHARINDEX('''',#sText)+1,
len(#sText)))
DECLARE #sText0 nvarchar(1000)
DECLARE #sColumn1 nvarchar(1000)
DECLARE #sColumn2 nvarchar(1000)
DECLARE #sColumn3 nvarchar(1000)
DECLARE #sColumn4 nvarchar(1000)
DECLARE #iCnt int = 1
while #iCnt<=4
--(0<len(#sText) and 0<#lBegin and 0<#lEnd)
BEGIN
SET #sText0 = substring(#sText,#lBegin+1,#lEnd-2)
IF #iCnt=1 begin SET #sColumn1=#sText0 end
IF #iCnt=2 begin SET #sColumn2=#sText0 end
IF #iCnt=3 begin SET #sColumn3=#sText0 end
IF #iCnt=4 begin SET #sColumn4=#sText0 end
set #sText = substring(#sText,#lBegin + #lEnd+2,len(#sText))
SET #lBegin = CHARINDEX('''',#sText)
SET #lEnd = charindex('''',
substring(#sText,
CHARINDEX('''',#sText)+1,
len(#sText)))
SET #iCnt = #iCnt+1
END
INSERT
tResults (sColumn1,sColumn2,sColumn3,sColumn4)
VALUES (#sColumn1,#sColumn2,#sColumn3,#sColumn4)
SELECT * FROM tResults
on sql fiddle
You will be able to achieve this using CHARINDEX() and SUBSTRING()
Following example shows for splitting to 2 columns. When it has more columns, query will be get little more complicated. However, you can follow this to build your query.
SELECT OriginalColumn
, SUBSTRING(OriginalColumn, 1,CHARINDEX('x',OriginalColumn,1)-1) AS Column1
, SUBSTRING(OriginalColumn, CHARINDEX('x',OriginalColumn,1) + 1 ,CHARINDEX('x',OriginalColumn,CHARINDEX('x',OriginalColumn,1)-1)) AS Column2
FROM YourTable
I have used "x" as the delimiter in the example. Following is a sample result
try this:
declare #delim char
set #delim = ''''
declare #str nvarchar(max)
declare #substr nvarchar(max)
declare #newstr nvarchar(max)
declare #tmpTable table (partStrings nvarchar(max))
declare #count int
set #count = 0
select #str = <***Your String***>
while(charindex(#delim,#str) != 0)
begin
set #count = #count + 1
Select #substr = substring(#str,1,charindex(#delim,#str)-1)
if((#count % 2) = 0)
begin
insert into #tmpTable values(#substr)
end
Set #newstr = substring(#str,charindex(#delim,#str)+1,len(#str)-charindex(#delim,#str))
set #str = #newstr
end
select partStrings from #tmpTable
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.