Wrap text in SQL server using function - sql-server

I googled a lot to wrap string with minimum defined length, but I am unable to get any solution.
I created my own function that can wrap text by given number of characters per line.
This post may help to others looking for same.

Function 1
Create FUNCTION [dbo].[fn_BraekTextInLines]
(
-- Add the parameters for the function here
#InString varchar(max),
#LineLength int
)
RETURNS nvarchar(max)
AS
BEGIN
if #LineLength <=0 or #LineLength> LEN(#InString)
return #InString
declare #tmp varchar(max)
declare #result varchar(max)
DECLARE #word varchar (max);
declare #addedInResult bit
DECLARE c CURSOR LOCAL STATIC READ_ONLY FORWARD_ONLY
FOR SELECT s FROM SplitMax(#InString,'');
OPEN c;
FETCH NEXT FROM c INTO #word;
--set #tmp =#word
WHILE ##FETCH_STATUS = 0
BEGIN
if LEN(#tmp + ' ' + #word) < #LineLength
begin
set #tmp = #tmp + ' ' + #word
set #addedInResult = 0
end
else
begin
set #result = isnull(#result, ' ') + CHAR(13) + RTRIM(LTRIM( #tmp))
set #tmp = #word
set #addedInResult = 1
end
FETCH NEXT FROM c INTO #word;
if ##FETCH_STATUS <> 0
begin
set #result = isnull(#result, ' ') + CHAR(13) + RTRIM(LTRIM( #tmp))
set #addedInResult = 1
end
END
CLOSE c;
DEALLOCATE c;
if #addedInResult=0
begin
set #result = isnull(#result, ' ') + CHAR(13) + RTRIM(LTRIM( #tmp))
end
return #result
END
Function 2
Create FUNCTION [dbo].[fn_WrapString]
(
-- Add the parameters for the function here
#InString varchar(max),
#LineLength int
)
RETURNS nvarchar(max)
AS
BEGIN
declare #result varchar(max)
declare #tmp varchar(max)
DECLARE #Line varchar (max);
DECLARE c CURSOR LOCAL STATIC READ_ONLY FORWARD_ONLY
FOR SELECT s FROM SplitMax(#InString,CHAR(13));
OPEN c;
FETCH NEXT FROM c INTO #Line;
WHILE ##FETCH_STATUS = 0
BEGIN
set #tmp = dbo.fn_BraekTextInLines(#Line,#LineLength)
set #result = isnull(#result,' ') + #tmp
FETCH NEXT FROM c INTO #Line;
END
CLOSE c;
DEALLOCATE c;
return Rtrim(Ltrim(#result))
END
Function 3
ALTER FUNCTION [dbo].[SplitMax](#String VARCHAR(max), #Delimiter CHAR(1))
RETURNS #temptable TABLE (s VARCHAR(max))
AS
BEGIN
DECLARE #idx INT
DECLARE #slice VARCHAR(max)
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(s) VALUES(#slice)
SET #String = RIGHT(#String,len(#String) - #idx)
IF len(#String) = 0 break
END
RETURN
END
Calling Function fn_WrapString to wrap the text
declare #name varchar(max)
set #name = 'Ine was King of Wessex from 688 to 726. He was'+ CHAR(13) +'unable to retain the territorial gains of his predecessor, Cædwalla, who had brought much of southern England under his'
print dbo.fn_WrapString(#name,60)
Output :
Ine was King of Wessex from 688 to 726. He was
unable to retain the territorial gains of his predecessor,
Cædwalla, who had brought much of southern England under
his

I also created a short version for warping the text in T-Sql
CREATE FUNCTION [dbo].[WrapText]
(
#List NVARCHAR(MAX),
#length INT
)
RETURNS NVARCHAR(MAX)
AS
BEGIN
DECLARE #result AS NVARCHAR(MAX);
SELECT #result = CASE
WHEN #result IS NULL THEN
SUBSTRING(#List, number.Number - #length + 1, #length)
ELSE
#result + CHAR(13) + CHAR(10) + SUBSTRING(#List, number.Number - #length + 1, #length)
END
FROM
(
SELECT Number = ROW_NUMBER() OVER (ORDER BY name)
FROM sys.all_objects
) number
WHERE number.Number <= LEN(#List) + #length - 1
AND number.Number % #length = 0;
RETURN #result;
END;
Calling Function:
SELECT dbo.WrapText('abcdefghijklmnopqrstuvwxyz',5)
If you are coping the result from grid, it will not past with line breaks as explained here
https://stackoverflow.com/a/59189881/1606054

Related

SQL Server : how to print variable breaking each line and going above varchar(MAX) [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.

Find word in a string with a character

I have tried so many times but could not find the exact query yet.
The one I made works in few string but doesn't work in another(It is uncertain).
What i want is the word which has '.' in it like "abcde sfjhjk.dkjb sajb njdhf", what i want is "sfjhjk.dkjb" as result . This is just an example.
The query returns all letters in some cases while truncates few digits in other cases. You can check by providing different values.
I tried below :
This doesn't work:
DECLARE #QUERY VARCHAR(MAX)='
renewreque0_.amount AS AMOUNT48_,
renewreque0_.charge_type AS CHARGE3_48_,
renewreque0_.commission_rate AS COMMISSION4_48_
'
SET NOCOUNT ON;
DECLARE #TABLENAME TABLE(TABLE_NAME VARCHAR(MAX),ALIAS VARCHAR(MAX))
DECLARE #COLUMNS_JOIN TABLE(COL VARCHAR(MAX),COLUMN_NAME VARCHAR(MAX),ALIAS VARCHAR(MAX))
DECLARE #NAME VARCHAR(MAX),#ALIAS VARCHAR(MAX),#J_QUERY VARCHAR(MAX),#W_QUERY VARCHAR(MAX)
DECLARE #WHERE_JOIN TABLE(COL VARCHAR(MAX),COLUMN_NAME VARCHAR(MAX),ALIAS VARCHAR(MAX))
WHILE CHARINDEX('.',#QUERY)>1
BEGIN
SET #NAME = REVERSE( SUBSTRING(REVERSE(#QUERY),CHARINDEX('.',REVERSE(#QUERY))+1,CHARINDEX(' ',#QUERY)) )
SET #ALIAS= REVERSE(LEFT(REVERSE(#QUERY),CHARINDEX('.',REVERSE(#QUERY))))
SET #ALIAS=LEFT(#ALIAS,CHARINDEX(' ',#ALIAS))
SET #NAME=LTRIM(RTRIM(#NAME))
SET #ALIAS=LTRIM(RTRIM(#ALIAS))
INSERT INTO #COLUMNS_JOIN SELECT #NAME+#ALIAS,#NAME,REVERSE(LEFT(REVERSE(#ALIAS),LEN(#ALIAS)-1))
SET #QUERY=REPLACE(#QUERY,#NAME+#ALIAS,'')
END
SELECT * FROM #COLUMNS_JOIN
This works:
DECLARE #QUERY VARCHAR(MAX)='
AND t8_.username LIKE ?
AND t4_.branch_id = ?
AND t1_.account_no = ?
AND t0_.remarks = ?
AND t0_.collect_from = ?
'
SET NOCOUNT ON;
DECLARE #TABLENAME TABLE(TABLE_NAME VARCHAR(MAX),ALIAS VARCHAR(MAX))
DECLARE #COLUMNS_JOIN TABLE(COL VARCHAR(MAX),COLUMN_NAME VARCHAR(MAX),ALIAS VARCHAR(MAX))
DECLARE #NAME VARCHAR(MAX),#ALIAS VARCHAR(MAX),#J_QUERY VARCHAR(MAX),#W_QUERY VARCHAR(MAX)
DECLARE #WHERE_JOIN TABLE(COL VARCHAR(MAX),COLUMN_NAME VARCHAR(MAX),ALIAS VARCHAR(MAX))
WHILE CHARINDEX('.',#QUERY)>1
BEGIN
SET #NAME = REVERSE( SUBSTRING(REVERSE(#QUERY),CHARINDEX('.',REVERSE(#QUERY))+1,CHARINDEX(' ',#QUERY)) )
SET #ALIAS= REVERSE(LEFT(REVERSE(#QUERY),CHARINDEX('.',REVERSE(#QUERY))))
SET #ALIAS=LEFT(#ALIAS,CHARINDEX(' ',#ALIAS))
SET #NAME=LTRIM(RTRIM(#NAME))
SET #ALIAS=LTRIM(RTRIM(#ALIAS))
INSERT INTO #COLUMNS_JOIN SELECT #NAME+#ALIAS,#NAME,REVERSE(LEFT(REVERSE(#ALIAS),LEN(#ALIAS)-1))
SET #QUERY=REPLACE(#QUERY,#NAME+#ALIAS,'')
END
SELECT * FROM #COLUMNS_JOIN
Can anybody please help.
I would first use an SplitString function (passing a blank space as delimiter), which returns as rows each word on a string, and then filter it to return just the words having a dot.
SQL Server 2016 already has one https://learn.microsoft.com/en-us/sql/t-sql/functions/string-split-transact-sql and on older SQL Servers you can build your own : Splitting the string in sql server
set #SQLStr varchar(512) = 'abcde sfjhjk.dkjb sajb njdhf';
select value from string_split(#SQLStr, ' ')
where charindex('.', value) > 0
Alright just for fun
declare #str nvarchar(100) = 'abcde sfjhjk.dkjb sajb njdhf',
#pointIndex int
SET #pointIndex = (SELECT CHARINDEX('.',#str) )
SELECT RTRIM(LTRIM(SUBSTRING(#str, #pointIndex - CHARINDEX(' ',REVERSE(LEFT(#str,#pointIndex))) +1,CHARINDEX(' ',REVERSE(LEFT(#str,#pointIndex)))) -- left side
+SUBSTRING(#str,#pointIndex +1, CHARINDEX( ' ', SUBSTRING(#str,#pointIndex,len(#str) - #pointIndex) ) -1 )))
Needless to say i would not recommend this option because it is really hard to maintain. As Marc said your best option here is to split your string for blanks and find the ones with a '.'
Now if you dont have SQLServer 2016 here is a split function for you :
CREATE function [dbo].[Split]
(
#string nvarchar(max),
#delimiter nvarchar(20)
)
returns #table table
(
[Value] nvarchar(max)
)
begin
declare #nextString nvarchar(max)
declare #pos int, #nextPos int
set #nextString = ''
set #string = #string + #delimiter
set #pos = charindex(#delimiter, #string)
set #nextPos = 1
while (#pos <> 0)
begin
set #nextString = substring(#string, 1, #pos - 1)
insert into #table
(
[Value]
)
values
(
#nextString
)
set #string = substring(#string, #pos + len(#delimiter), len(#string))
set #nextPos = #pos
set #pos = charindex(#delimiter, #string)
end
return
end
And use it as such :
SELECT * FROM dbo.Split(REPLACE(#str,' ','/'),'/')
WHERE charindex('.', value) > 0
Note that i replace blanks by another value.

Return first five numbers form string within UDF

I am working on SQL Server (2005,2008 & 2012)
I wanna extract first five numbers from varchar column via using UDF
Input:
rrr123ddd4567ddd19828www2
123hhhsss124ss18762s
qq12349wsss12376ss
Output:
19828
18762
12349
My Trail is as following:
DECLARE
#myString VARCHAR(1000),
#temp VARCHAR(100),
#position INT,
#ExecuteInsert nvarchar (500),
#FirstChar bit
SET #myString = 'rrr123ddd4567ddd19828www2'
SET #position = 1
SET #FirstChar = 1
WHILE #position <= LEN(#myString)
BEGIN
IF (ISNUMERIC(SUBSTRING(#myString,#position,1))) = 1
BEGIN
SET #temp = isnull(#temp,'') + SUBSTRING(#myString,#position,1)
SET #FirstChar = 1
END
ELSE /* The char is alphabetical */
BEGIN
if (#FirstChar= 1)
BEGIN
SET #temp = isnull(#temp,'') + ','
SET #FirstChar = 0
END
END
SET #position = #position + 1
END
IF (RIGHT(#temp,1) <> ',')
BEGIN
SET #temp = #temp + ','
END
SELECT #temp = REPLACE(','+ #temp + ',',',,','')
SELECT #temp = Replace (#temp,',','''),(''')
Select #temp = '(''' + #temp + ''')'
Create table #temp
(
col1 varchar(100)
)
SET #ExecuteInsert = 'insert into #temp values ' + #temp
Execute sp_executesql #ExecuteInsert
select top 1 col1 from #temp
where LEN(col1) = 5
drop table #temp
-- Output >> 19828
The previous query is working well with string input , but I wanna using this code within UDF to could using it with columns.
if I used the previous query within UDF, the following error is raising:
Cannot access temporary tables from within a function.
EDIT
if I used Table variable , I get the next error:
Only functions and some extended stored procedures can be executed
from within a function.
any help will be greatly appreciated.
CREATE FUNCTION udfTest
(
-- Add the parameters for the function here
)
RETURNS int
AS
BEGIN
-- Declare the return variable here
DECLARE
#Result int,
#myString VARCHAR(1000),
#temp VARCHAR(100),
#position INT,
#ExecuteInsert nvarchar (500),
#FirstChar bit
SET #myString = 'rrr123ddd4567ddd19828www2'
SET #position = 1
SET #FirstChar = 1
WHILE #position <= LEN(#myString)
BEGIN
IF (ISNUMERIC(SUBSTRING(#myString,#position,1))) = 1
BEGIN
SET #temp = isnull(#temp,'') + SUBSTRING(#myString,#position,1)
SET #FirstChar = 1
END
ELSE /* The char is alphabetical */
BEGIN
if (#FirstChar= 1)
BEGIN
SET #temp = isnull(#temp,'') + ','
SET #FirstChar = 0
END
END
SET #position = #position + 1
END
IF (RIGHT(#temp,1) <> ',')
BEGIN
SET #temp = #temp + ','
END
SELECT #temp = REPLACE(','+ #temp + ',',',,','')
SELECT #temp = Replace (#temp,',','''),(''')
Select #temp = '(''' + #temp + ''')'
Declare #tempTable TABLE
(
col1 varchar(100)
)
insert into #tempTable SELECT #temp
select top 1 #Result=col1 from #tempTable
where LEN(col1) = 5
return #Result
END
GO
Here you are my answer of my question , hope helps others.
The objective is creating UDF function for using it with columns, not only fixed values.
The approach is using SplitString instead of sp_executesql
for splitting a comma separated string and loop it's values in table.
Demo:-
Create table DummyTable
( col1 varchar (100))
go
Insert into DummyTable values ('rrr123ddd4567ddd19828www2')
Insert into DummyTable values ('123hhhsss124ss18762s')
Insert into DummyTable values ('qq12349wsss12376ss')
go
/*
SplitString via Mudassar Khan
http://www.aspsnippets.com/Articles/Split-and-convert-Comma-Separated-Delimited-String-to-Table-in-SQL-Server.aspx
*/
Create FUNCTION SplitString
(
#Input NVARCHAR(MAX),
#Character CHAR(1)
)
RETURNS #Output TABLE (
Item NVARCHAR(1000)
)
AS
BEGIN
DECLARE #StartIndex INT, #EndIndex INT
SET #StartIndex = 1
IF SUBSTRING(#Input, LEN(#Input) - 1, LEN(#Input)) <> #Character
BEGIN
SET #Input = #Input + #Character
END
WHILE CHARINDEX(#Character, #Input) > 0
BEGIN
SET #EndIndex = CHARINDEX(#Character, #Input)
INSERT INTO #Output(Item)
SELECT SUBSTRING(#Input, #StartIndex, #EndIndex - 1)
SET #Input = SUBSTRING(#Input, #EndIndex + 1, LEN(#Input))
END
RETURN
END
GO
-------------------------------------
-------------------------------------
-------------------------------------
/*
My Own Function
*/
Create FUNCTION udfGetFirstFiveNumbers
(
#myString VARCHAR(1000)
)
RETURNS varchar(100)
AS
BEGIN
DECLARE
#temp VARCHAR(100),
#result Varchar (100),
#position INT,
#ExecuteInsert nvarchar (500),
#FirstChar bit
SET #position = 1
SET #FirstChar = 1
WHILE #position <= LEN(#myString)
BEGIN
IF (ISNUMERIC(SUBSTRING(#myString,#position,1))) = 1
BEGIN
SET #temp = isnull(#temp,'') + SUBSTRING(#myString,#position,1)
SET #FirstChar = 1
END
ELSE /* The char is alphabetical */
BEGIN
if (#FirstChar= 1)
BEGIN
SET #temp = isnull(#temp,'') + ','
SET #FirstChar = 0
END
END
SET #position = #position + 1
END
IF (RIGHT(#temp,1) <> ',')
BEGIN
SET #temp = #temp + ','
END
SELECT #temp = REPLACE(','+ #temp + ',',',,','')
SELECT #result = Item
FROM dbo.SplitString(#temp, ',')
where len(Item) = 5
return #result
END
GO
-- Test
select col1, dbo.udfGetFirstFiveNumbers(col1) as result
from DummyTable
Result:-

Transform text in SQL Server

I am trying to create a dynamic query in SQL Server.
Input: #value= abc,def,en,
Output: MAX(abc) as abc, MAX(def) as def, MAX(en) as en
My efforts so far took me no where.
With CONVERT() and REPLACE() I achieved a bit but finding it difficult. Need help!
Try this:
declare #value varchar(50) = 'abc,def,en'
declare #result varchar(100) = ''
select #result = replace(#value,'abc', 'MAX(''abc'') as abc')
select #result = replace(#result,'def', 'MAX(''def'') as def')
select #result = replace(#result,'en', 'MAX(''en'') as en')
select #result
You can also do the replacements in one line by nesting the expressions.
EDIT: If you have variable values in #value, you can take the below approach:
Use a splitter function to get the individual values in the string as a list. You can take a look at this article for implementations.
Insert this list to a temp table.
Update the temp table as shown above.
Concatenate the values into a single string using STUFF like so:
select stuff((select ',' + val from #temp for xml path('')),1,1,'')
Try this:
DECLARE #Value VARCHAR(200) = 'abc,def,en'
DECLARE #Template VARCHAR(100) = 'MAX(''##'') as ##'
DECLARE #Result VARCHAR(1000) = ''
DECLARE #Data VARCHAR(100) = ''
WHILE LEN(#Value) > 0
BEGIN
SET #Data = REPLACE(LEFT(#Value, ISNULL(NULLIF(CHARINDEX(',', #Value),0), LEN(#Value))),',','')
SET #Result = #Result + REPLACE(#Template, '##', #Data)
IF CHARINDEX(',', #Value) > 0
BEGIN
SET #Result = #Result + ','
SET #Value = REPLACE(#Value,#Data + ',','')
END
ELSE
SET #Value = REPLACE(#Value,#Data,'')
END
SELECT #Result
Have a look at SQL User Defined Function to Parse a Delimited String
So you can do like
Declare #Value varchar(200) = 'abc,def,en'
Declare #Item varchar(20) = null
declare #Str varchar(1000)=''
WHILE LEN(#Value) > 0
BEGIN
IF PATINDEX('%,%',#Value) > 0
BEGIN
SET #Item = SUBSTRING(#Value, 0, PATINDEX('%,%',#Value))
-- SELECT #Item
IF(LEN(#Str)>0)
SET #Str = #Str + ', SELECT MAX('+#Item+') as ' +#Item
ELSE
SET#Str = #Str + ' SELECT MAX('+#Item+') as ' +#Item
SET #Value = SUBSTRING(#Value, LEN(#Item + ',') + 1, LEN(#Value))
END
ELSE
BEGIN
SET #Item = #Value
SET #Value = NULL
SET #Str = #Str + 'SELECT MAX('+#Item+') as ' + #Item
END
END
select #Str
See the fiddle sample here

A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations

What is wrong with this statement?
ALTER Function [InvestmentReturn].[getSecurityMinLowForPeriod](#securityid int,
#start datetime,
#end datetime)
returns xml
begin
declare #results varchar(500)
declare #low int
declare #adjustedLow int
declare #day varchar(10)
if #end is null
begin
set #end = getdate()
end
set #adjustedLow = (select min(adjLow)
from (
select Low * [InvestmentReturn].[fn_getCorporateActionSplitFactor](isq.securityid, #start, convert(varchar,day, 111)) as adjLow
from
securityquote isq
where isq.securityid = #securityid and isq.day >= convert(varchar(10), #start, 111) and convert(varchar(10), #end, 111) >= isq.day
and low != -1
) as x)
select
top 1 #low = low
, #day = day
, #adjustedLow
--select high
from
securityquote sq
where
day >= convert(varchar(10), #start, 111) and convert(varchar(10), #end, 111) >= day
and securityid = #securityid and low != -1
order by low asc
set #results= '<results type="debug_min">'
set #results = #results + '<periodStart>' + coalesce(cast(#start as varchar(20)), 'NULL') + '</periodStart>'
set #results = #results + '<periodEnd>' + coalesce(cast(#end as varchar(20)), 'NULL') + '</periodEnd>'
set #results = #results + '<securityID>' + coalesce(cast(#securityID as varchar(10)), 'NULL') + '</securityID>'
set #results = #results + '<periodMin>' + coalesce(cast(#low as varchar(10)), '-11111') + '</periodMin>'
set #results = #results + '<coraxAdjustedPeriodMin>' + coalesce(cast(#adjustedLow as varchar(10)), '-11111') + '</coraxAdjustedPeriodMin>'
set #results = #results + '<dayMinOcurred>' + coalesce(#day, 'NULL') + '</dayMinOcurred>'
set #results = #results + '</results>'
return #results
Just to explain the answer (after getting where the error was caused), I simply removed #adjustedLow from the second select statement.
Column values from the SELECT statement are assigned into #low and #day local variables; the #adjustedLow value is not assigned into any variable and it causes the problem:
The problem is here:
select
top 1 #low = low
, #day = day
, #adjustedLow -- causes error!
--select high
from
securityquote sq
...
Detailed explanation and workaround: SQL Server Error Messages - Msg 141 - A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations.
You cannot use a select statement that assigns values to variables to also return data to the user
The below code will work fine, because i have declared 1 local variable and that variable is used in select statement.
Begin
DECLARE #name nvarchar(max)
select #name=PolicyHolderName from Table
select #name
END
The below code will throw error "A SELECT statement that assigns a value to a variable
must not be combined with data-retrieval operations" Because we are retriving data(PolicyHolderAddress) from table, but error says data-retrieval operation is not allowed when you use some local variable as part of select statement.
Begin
DECLARE #name nvarchar(max)
select
#name = PolicyHolderName,
PolicyHolderAddress
from Table
END
The the above code can be corrected like below,
Begin
DECLARE #name nvarchar(max)
DECLARE #address varchar(100)
select
#name = PolicyHolderName,
#address = PolicyHolderAddress
from Table
END
So either remove the data-retrieval operation or add extra local variable. This will resolve the error.
declare #cur cursor
declare #idx int
declare #Approval_No varchar(50)
declare #ReqNo varchar(100)
declare #M_Id varchar(100)
declare #Mail_ID varchar(100)
declare #temp table
(
val varchar(100)
)
declare #temp2 table
(
appno varchar(100),
mailid varchar(100),
userod varchar(100)
)
declare #slice varchar(8000)
declare #String varchar(100)
--set #String = '1200096,1200095,1200094,1200093,1200092,1200092'
set #String = '20131'
select #idx = 1
if len(#String)<1 or #String is null return
while #idx!= 0
begin
set #idx = charindex(',',#String)
if #idx!=0
set #slice = left(#String,#idx - 1)
else
set #slice = #String
--select #slice
insert into #temp values(#slice)
set #String = right(#String,len(#String) - #idx)
if len(#String) = 0 break
end
-- select distinct(val) from #temp
SET #cur = CURSOR FOR select distinct(val) from #temp
--open cursor
OPEN #cur
--fetchng id into variable
FETCH NEXT
FROM #cur into #Approval_No
--
--loop still the end
while ##FETCH_STATUS = 0
BEGIN
select distinct(Approval_Sr_No) as asd, #ReqNo=Approval_Sr_No,#M_Id=AM_ID,#Mail_ID=Mail_ID from WFMS_PRAO,WFMS_USERMASTER where WFMS_PRAO.AM_ID=WFMS_USERMASTER.User_ID
and Approval_Sr_No=#Approval_No
insert into #temp2 values(#ReqNo,#M_Id,#Mail_ID)
FETCH NEXT
FROM #cur into #Approval_No
end
--close cursor
CLOSE #cur
select * from #tem

Resources