Spliting large string in Sql Server - sql

I am using this logic to split the string query
declare #query nvarchar(max)
set #query = '1&2&3&4&5&6&7&8&9&10&11&12&13&14'
SELECT SUBSTRING('&' + #query + '&', Number + 1, -- is used to split the '#query' on the basis of '&' sign
CHARINDEX('&', '&' + #query + '&', Number + 1) - Number -1)AS VALUE
FROM master..spt_values
WHERE Type = 'P'
AND Number <= LEN('&' + #query + '&') - 1
AND SUBSTRING('&' + #query + '&', Number, 1) = '&'
It works fine when query is small, but its giving me less result then actual when the value of #query is very large
For eg.
#query = 'very large string containing 60 & sign '
returns only 10 records
How can I split large string, and what is the reason? Why can SUBSTRING not handle large strings?

You can use this function. It returns a table of splitted values, based on input string and a delimeter.
Usage:
select *
from dbo.fn_ParseText2Table('1&2&3&4&5&6&7&8&9&10&11&12&13&14','&')
The function has a parameter #p_SourceText which type is varchar(8000), so input string can be up to 8000 characters.
You can change type to varchar(max) if version of your SQL Server allows you to do this.

I used this Function and its working for me Perfectly :)
CREATE function f_split(#SourceSql nvarchar(Max),#StrSeprate varchar(10))
returns #temp
table([column] nvarchar(Max))
begin
declare #i int
set #SourceSql=rtrim(ltrim(#SourceSql))
set #i=charindex(#StrSeprate,#SourceSql)
while #i>=1
begin
insert #temp values(left(#SourceSql,#i-1))
set #SourceSql=substring(#SourceSql,#i+1,len(#SourceSql)-#i)
set #i=charindex(#StrSeprate,#SourceSql)
end
if #SourceSql<>'\'
insert #temp values(#SourceSql)
return
end
go
select * from dbo.f_split('1,2,3,4,5',',')
go

I am not sure why your code is not getting the result. I executed the query with large amount of data. But I got the result. May be your string is very much bigger than I did the testing. I have also the same requirement to split the string. I am using this function to get the solution. You can try this.
CREATE FUNCTION [dbo].[fnSplitString] ( #MyArray VARCHAR(8000), #separator CHAR(1) )
RETURNS #RetTable TABLE
(StrValue VARCHAR(256))
AS
BEGIN
DECLARE #SeperatorString VARCHAR(10);
SET #SeperatorString = '%' + #separator + '%'
DECLARE #separator_position INT
DECLARE #array_value VARCHAR(1000)
SET #MyArray = #MyArray + #separator
WHILE PATINDEX( #SeperatorString , #MyArray) <> 0
BEGIN
SELECT #separator_position = PATINDEX(#SeperatorString , #MyArray)
SELECT #array_value = LEFT(#MyArray, #separator_position - 1)
INSERT #RetTable VALUES ( CAST(#array_value AS VARCHAR(256)) )
SELECT #MyArray = STUFF(#MyArray, 1, #separator_position, '')
END
RETURN
END
If you want more explanation on this function, how the function is using and what the parameters are , you can take a look here.. This is very simple function and easy to use.

Related

how to find all strings in between commas in sql server

I want to display a string in a table format as shown below:
For a string like 'hi,is,1,question,thanks,.,.,n'
I need this result:
column1 column2 column3 column4 ..... column
hi is 1 question ..... n
DECLARE #string VARCHAR(MAX);
SET #string = 'hi,is,1,question,thanks,.,.,n';
DECLARE #SQL VARCHAR(MAX);
SET #SQL = 'SELECT ''' + REPLACE(#string, ',', ''',''') + '''';
EXEC (#SQL);
Result:
Add SELECT ' at beginning and ' at the end of string
Replace all , with ',' inside string
So string 'hi,is,1,question,thanks,.,.,n' is replace by 'SELECT 'hi','is','1','question','thanks','.','.','n''
Executed as SQL query
PS: If you want to use it on column you will have to combine it with CURSOR
Update
DECLARE #table TABLE
(
ID INT IDENTITY,
string VARCHAR(MAX)
);
INSERT INTO #table
VALUES
('This,is,a,string,,n,elements,..');
INSERT INTO #table
VALUES
('And,one,more');
INSERT INTO #table
VALUES
('Ugly,but,works,,,Yay!,..,,,10,11,12,13,14,15,16,17,18,19,..');
SELECT * FROM #table
DECLARE #string_to_split VARCHAR(MAX);
DECLARE #sql_query_to_execute VARCHAR(MAX);
DECLARE #max_elements INT, #id INT, #i INT;
SET #i = 1;
DECLARE string_cursor CURSOR FOR SELECT ID, string FROM #table;
SELECT #max_elements = MAX(LEN(string) - LEN(REPLACE(string, ',', ''))) + 1 -- Find max number of elements */
FROM #table;
IF OBJECT_ID('tempdb..##my_temp_table_for_splitted_columns') <> 0 -- Create new temp table with valid amount of columns
DROP TABLE ##my_temp_table_for_splited_columns;
SET #sql_query_to_execute = 'create table ##my_temp_table_for_splitted_columns ( ID int,';
WHILE #i <= #max_elements
BEGIN
SET #sql_query_to_execute = #sql_query_to_execute + ' Col' + CAST(#i AS VARCHAR(max)) + ' varchar(25), ';
SET #i = #i + 1;
END;
SELECT #sql_query_to_execute = SUBSTRING(#sql_query_to_execute, 1, LEN(#sql_query_to_execute) - 1) + ')';
EXEC (#sql_query_to_execute);
/* Split string for each row */
OPEN string_cursor;
FETCH NEXT FROM string_cursor
INTO #id,
#string_to_split
WHILE ##FETCH_STATUS = 0
BEGIN
SET #i = MAX(LEN(#string_to_split) - LEN(REPLACE(#string_to_split, ',', ''))) + 1; -- check amount of columns for current string
WHILE #i < #max_elements
BEGIN
SET #string_to_split = #string_to_split + ','; -- add missing columns
SET #i = #i + 1;
END;
SET #sql_query_to_execute = 'SELECT ' + CAST(#id AS VARCHAR(MAX)) + ',''' + REPLACE(#string_to_split, ',', ''',''') + '''';
INSERT INTO ##my_temp_table_for_splitted_columns --insert result to temp table
EXEC (#sql_query_to_execute);
FETCH NEXT FROM string_cursor
INTO #id,
#string_to_split;
END;
CLOSE string_cursor;
DEALLOCATE string_cursor;
SELECT *
FROM ##my_temp_table_for_splitted_columns;
This is not trivial. You will find a lot of examples how to split your string in a set of fragments. And you will find a lot of examples how to pivot a row set to a single row. But - adding quite some difficulty - you have an unknown count of columns. There are three approaches:
Split this and return your set with a known maximum of columns
Use a dynamically created statement and use EXEC. But this will not work in VIEWs or iTVFs, nor will it work against a table.
Instead of a column list you return a generic container like XML
with a known maximum of columns
One example for the first was this
DECLARE #str VARCHAR(1000)='This,is,a,string,with,n,elements,...';
SELECT p.*
FROM
(
SELECT A.[value]
,CONCAT('Column',A.[key]+1) AS ColumnName
FROM OPENJSON('["' + REPLACE(#str,',','","') + '"]') A
) t
PIVOT
(
MAX(t.[value]) FOR ColumnName IN(Column1,Column2,Column3,Column4,Column5,Column6,Column7,Column8,Column9 /*add as many as you need*/)
) p
Hint: My approach to split the string uses OPENJSON, not available before version 2016. But there are many other approaches you'll find easily. It's just an example to show you the combination of a splitter with PIVOT using a running index to build up a column name.
Unknown count of columns
And the same example with a dynamically created column list was this:
DECLARE #str VARCHAR(1000)='This,is,a,string,with,n,elements,...';
DECLARE #CountElements INT=LEN(#str)-LEN(REPLACE(#str,',',''))+1;
DECLARE #columnList NVARCHAR(MAX)=
STUFF((
SELECT TOP(#CountElements)
CONCAT(',Column',ROW_NUMBER() OVER(ORDER BY (SELECT 1)))
FROM master..spt_values /*has a lot of rows*/
FOR XML PATH('')
),1,1,'');
DECLARE #Command NVARCHAR(MAX)=
N'SELECT p.*
FROM
(
SELECT A.[value]
,CONCAT(''Column'',A.[key]+1) AS ColumnName
FROM OPENJSON(''["'' + REPLACE(''' + #str + ''','','',''","'') + ''"]'') A
) t
PIVOT
(
MAX(t.[value]) FOR ColumnName IN(' + #columnList + ')
) p;';
EXEC(#Command);
Hint: The statement created is exactly the same as above. But the column list in the pivot's IN is created dynamically. This will work with (almost) any count of words generically.
If you need more help, please use the edit option of your question and provide some more details.
An inlineable approach for a table returning a generic container
If you need this against a table, you might try something along this:
DECLARE #tbl TABLE(ID INT IDENTITY,YourList NVARCHAR(MAX));
INSERT INTO #tbl VALUES('This,is,a,string,with,n,elements,...')
,('And,one,more');
SELECT *
,CAST('<x>' + REPLACE((SELECT t.YourList AS [*] FOR XML PATH('')),',','</x><x>') + '</x>' AS XML) AS Splitted
FROM #tbl t
This will return your list as an XML like
<x>This</x>
<x>is</x>
<x>a</x>
<x>string</x>
<x>with</x>
<x>n</x>
<x>elements</x>
<x>...</x>
You can grab - if needed - each element by its index like here
TheXml.value('/x[1]','nvarchar(max)') AS Element1

Variable in query doesn't work

I'm facing some problem with a SQL query, I'm creating a variable :
declare #material varchar(500)
declare #typ varchar(200)
declare #strsql varchar(max)
set #typ = 'papier'
select #material = (SELECT + rtrim(ltrim([grupa])) + ','
FROM [test].[dbo].[GT]
WHERE (typ = #typ) FOR XML PATH(''))
set #material = left(#material, len(#material)-1)
set #material = replace(#material, ',' ,''',''')
set #material = '''' + #material + ''''
select #material
The output from variable is :
'test','test2'
And here is a little part of my main code :
SELECT
Number,
isnull(sum((case
when [Group] in ('test','test2')
then isnull(cast([Quantity] as int), 0)
end)), 0) as other
FROM
[dbo].[test-table]
which works correct but when I'm trying to do this like that :
SELECT
Number,
isnull(sum((case
when [Group] in (#material)
then isnull(cast([Quantity] as int), 0)
end)), 0) as other
FROM
[dbo].[test-table]
Output is wrong (different). Can anyone tell me why? It's kinda this same.
It's kind of the same, but it's not the same. What you are looking to get is: in ('test','test2'), what you get is: in (''test','test2'').
You will have to build the rest of your query dynamically, something like:
DECLARE #SQL NVARCHAR(MAX) = 'SELECT
Number
,isnull(sum((case when [Group] in ('+ #material + ')
then isnull(cast([Quantity] as int),0) end)),0) as other
from [dbo].[test-table]'
EXEC sys.sp_executesql #SQL
If you say the output is wrong, it's best to show the actual output and why it is wrong. Saying "Output is different comparing to first query - it should be the same" is not enough, in most cases.
edit:
If you don't want to use dynamic sql (which makes sense), you could do something like this (which is actually much more readable than dynamic):
SELECT Number
, ISNULL(SUM(data),0) as other
FROM (
SELECT Number
, CASE WHEN [Group] IN (SELECT grupa FROM [dbo].[GT] WHERE typ = #typ)
THEN ISNULL(CAST([Quantity] AS INT), 0)
END data
FROM [dbo].[test-table]
) TT
GROUP BY Number
I am making a lot of assumptions about your goal, your schema and your data here though. Without more info, this is probably the best I can do...(There may be some performance tweaking to be done, but this is the essence)
Try to use a function for split the string:
CREATE FUNCTION [dbo].[FN_SplitString](#String nvarchar(4000), #Delimiter char(1))
RETURNS #Results TABLE (Items nvarchar(4000))
AS
BEGIN
IF #String IS NULL RETURN
IF LTRIM(RTRIM(#String)) = '' RETURN
DECLARE #INDEX INT
DECLARE #SLICE nvarchar(4000)
SELECT #INDEX = 1
WHILE #INDEX !=0
BEGIN
SELECT #INDEX = CHARINDEX(#Delimiter,#STRING)
IF #INDEX !=0
SELECT #SLICE = LEFT(#STRING,#INDEX - 1)
ELSE
SELECT #SLICE = #STRING
INSERT INTO #Results(Items) VALUES(#SLICE)
SELECT #STRING = RIGHT(#STRING,LEN(#STRING) - #INDEX)
IF LEN(#STRING) = 0 BREAK
END
RETURN
Then use it in this way:
SELECT
Number
,isnull(sum((case when [Group] in (SELECT Items, FROM FN_SplitString(#material,','))
then isnull(cast([Quantity] as int),0) end)),0) as other
from [dbo].[test-table]
In SQL Server, if you pass a comma separated list as a variable in IN Clause in T-SQL, it would not give error but you will not even get expected result either.
There are two solutions to handle this:
Using Dynamic query
Using Split function

Using a comma-separated parameter in an IN clause

I have 'param1, param2, parma3' coming from SSRS to a stored procedure as a varchar parameter: I need to use it in a query's IN clause but then need to change its format like this first:
select *
from table1
where col1 in('param1', 'param2', 'param3')
What is the best way to reformat the parameter without creating functions and parameter tables?
Try this one, Just need to add commas at the beginning and at the end of #params string.
Declare #params varchar(100) Set #params = ',param1,param2,param3,'
Select * from t
where CHARINDEX(','+cast(col1 as varchar(8000))+',', #params) > 0
SQL FIDDLE
you can use split function and use it as in following way
here my split fnSplitString return splitdata
select * from tb1 where id in(select splitdata from dbo.fnSplitString((select col1 from tb12 where id=3),','))
create FUNCTION [dbo].[fnSplitString]
(
#string NVARCHAR(MAX),
#delimiter CHAR(1)
)
RETURNS #output TABLE(splitdata NVARCHAR(MAX)
)
BEGIN
DECLARE #start INT, #end INT
SELECT #start = 1, #end = CHARINDEX(#delimiter, #string)
WHILE #start < LEN(#string) + 1 BEGIN
IF #end = 0
SET #end = LEN(#string) + 1
INSERT INTO #output(splitdata)
VALUES(SUBSTRING(#string, #start, #end - #start))
SET #start = #end + 1
SET #end = CHARINDEX(#delimiter, #string, #start)
END
RETURN
END
If you are using SQL 2016 and above string_split you can use.
-- #param is where you keep your comma separated values example:
declare #param = 'param1,param2,param3'
select * from table1 where col1 in (select TRIM(value) from string_split(#param,',')
More information about string_split check offical documemt
Furthermore, TRIM() is used to trim values from white spaces.
We can use STRING_SPLIT() in SQL SERVER
DECLARE #params varchar(max)= 'param1,param2,param3'
SELECT *
FROM table1
WHERE col1 IN (SELECT value FROM STRING_SPLIT( #params , ','))
"Best way" is arguable, but one classic approach that remains without "creating functions and table parameters" is to simply employ dynamic SQL in the stored procedure:
-- FORNOW: local to act as the SP param and arg
declare #values varchar(100) = 'param1, param2, param3'
-- Add opening and closing single quotes, then quotes around each
-- comma-separated list item.
select #values = '''' + REPLACE(#values, ', ', ''', ''') + ''''
-- FORNOW: for clarity/debugging
print #values
--'param1', 'param2', 'param3'
-- Run the desired query as dynamic SQL.
DECLARE #sql as nvarchar(250);
SET #sql = 'select * from table1 where col1 in (' + #values + ')';
EXEC sp_executesql #sql;
This assumes a couple things, though:
That commas in the list of values are followed by a space. Variations on this solution can address deviations in this respect of course, but it is important to be aware of this assumption.
That the comma-separated values do not themselves have commas in them – unlikely but worth mentioning since whether values will satisfy this constraint sometimes goes unconsidered.
Load the Params into a string and execute as an sql :
declare #param varchar(1000) = 'param1, param2, parma3'
declare #sql varchar(4000)
select #sql =
'select *
from table1
where col1 in(''' + replace(#param,',',''',''') + ''')'
-- print #sql -- to see what you're going to execute
exec sp_executesql #sql
DECLARE #params varchar(max) = '1,2,3,4'
SELECT * FROM table2 WHERE colId IN (SELECT value FROM SPLIT(#params,','))
Base on id we can find.

UDF to insert a text string in to each row of a table

I've been searching high and low for an answer and I can't seem to find anything that points me in the right direction.
I need to create a UDF that will extract each word of a text string and return a table with each word of the string in a separate row.
The UDF is only able to take in one variable '#mytext'.
We can assume that the text string is separated by a single space and may contain commas or periods.
For example, "don’t worry about failures, worry about the chances you miss when you don’t even try." would need to return a table with each word on a separate row of a column without the commas or periods present.
I'm figuring that the text string would need to be separated with a common value that could be used to separate each word for insert, but I could totally be wrong.
Any help with this would be really appreciated!
Based on what I've said so far, here is my far from complete code that I'm not too sure how to proceed with
create function [dbo].[textConverter]
(
#mytext nvarchar(max)
)
returns #text_string table
(
word nvarchar
)
as
begin
set #mytext = replace(#mytext, 'what needs to be changed', 'what it needs to be changed too')
--insert string to table
end
EDIT
I've checked out a couple of links and uncovered a little more information on this, I've now got this code. However it exits with an error. The example that was used in the article I found the code on used numbers in the insert so maybe this is the issue??
create function [dbo].[textConverter]
(
#mytext varchar(max)
)
returns #text_string table
(
word nvarchar
)
as
begin
--Change string to be seperated by commas
set #mytext = replace(#mytext, ' ', ',')
set #mytext = replace(#mytext, '.',',')
--Eliminate double commas
set #mytext = replace(#mytext, ',,', ',')
declare #name nvarchar(255)
declare #pos int
while CHARINDEX(',', #mytext) > 0
begin
select #pos = CHARINDEX(',', #mytext)
select #name = SUBSTRING(#mytext, 1, #pos-1)
insert into #text_string
select #name
select #mytext = SUBSTRING(#mytext, #pos+1, LEN(#mytext)-#pos)
end
insert into #text_string
select #mytext
return
end
--To use function
select * from dbo.textConverter('don’t worry about failures, worry about the chances you miss when you don’t even try.')
See the answer below, It is not in complete shape, but can be developed into a user defined function.
Declare #Sentence Varchar(max) = 'don’t worry about failures, worry about the chances you miss when you don’t even try.'
Set #Sentence = Replace(Replace(Replace(Replace(#Sentence,',',' '),'.',' '),' ',' '),' ',' ')
Declare #e int = (Select Len(#Sentence) - Len(Replace(#Sentence,' ','')))
Declare #s int = 1
Declare #Result Table(id int identity(1,1),Words varchar(max))
--Select #s,#e
While #s <= #e
begin
Insert into #Result
Select Left(#Sentence,Charindex(' ',#Sentence,1)-1)
Set #Sentence = Substring(#Sentence,Charindex(' ',#Sentence,1) + 1,Len(#Sentence) )
Set #s = #s + 1
End
Insert into #Result
Select #Sentence
Select * from #Result
Result
----+-----------
id |Words
----+-----------
1 |don’t
2 |worry
3 |about
4 |failures
5 |worry
6 |about
7 |the
8 |chances
9 |you
10 |miss
11 |when
12 |you
13 |don’t
14 |even
15 |try
----+-----------
I adapted some of the code from http://sqlperformance.com/2012/07/t-sql-queries/split-strings as my situation meant that the delimiter couldn't specified as input. I could only use on input and that was the text string. As such, the following worked for me:
create function [dbo].[textConverter]
(
#string nvarchar(max)
)
returns #output table(splitdata nvarchar(max)
)
begin
--Change string to be seperated by commas
set #string = replace(#string, ' ', ',')
set #string = replace(#string, '.',',')
--Eliminate double commas
set #string = replace(#string, ',,', ',')
declare #start int, #end int
select #start = 1, #end = charindex(',',#string)
while #start < len(#string) + 1
begin
if #end = 0
set #end = len(#string) + 1
insert into #output (splitdata)
values(substring(#string, #start, #end - #start))
set #start = #end + 1
set #end = charindex(',', #string, #start)
end
return
end

tsql IN not working properly if passed as parameter

I have a sitution where I need to pass a string in order to do an IN
declare #searchString varchar(50)
set #searchString = ' ''Nestle'',''UFI'' '
select * from tbl1 where CompanyName IN (#SearchString)
does't work.
But if I do:
select * from tbl1 where CompanyName IN ('Nestle','UFI')
It works fine. I cannot understand why one work and the other doesn't
When you use IN it looks at a set and not a single string expression. Because of this
you need to implement a function such as CsvToInt to return a table, like so:
CREATE Function [dbo].[CsvToInt] ( #Array varchar(1000))
returns #IntTable table
(IntValue int)
--Parse comma seperated value parameters
--To be used in SELECT blah WHERE blah IN (...)
--This function returns int, but may be modified to return any datatype
AS
begin
declare #separator char(1)
set #separator = ','
declare #separator_position int
declare #array_value varchar(1000)
set #array = #array + ','
while patindex('%,%' , #array) <> 0
begin
select #separator_position = patindex('%,%' , #array)
select #array_value = left(#array, #separator_position - 1)
Insert #IntTable
Values (Cast(#array_value as int))
select #array = stuff(#array, 1, #separator_position, '')
end
return
end
And then you can use this function from say within a stored procedure like so:
SELECT Blah
FROM MyTable
WHERE Foo IN (SELECT * FROM dbo.CsvToInt(#Parameter))
Where #Parameter contains a comma seperated string of values like:
Nestle, UFI, Test
IN spects a set, not an string as parameter.
See this:
http://social.msdn.microsoft.com/forums/en-US/transactsql/thread/33108337-b7b4-4ada-a480-60673e175f4d/
Your example that works is not the same as your example that does not work. Your example that works has a list of strings. Your example that does not work is just a single string. This means that you are trying to retrieve rows where the CompanyName field is equal to the whole string, not the comma delimited portions of that string.
Your example that does not work is equal to:
select * from tbl1 where CompanyName IN (' ''Nestle'',''UFI'' ')
not the following:
select * from tbl1 where CompanyName IN ('Nestle','UFI')
declare #searchString varchar(50)
set #searchString = ' ''Nestle'',''UFI'' '
exec ('select * from tbl1 where CompanyName IN (' + #SearchString + ');')
Note that this is dynamic SQL which is sometimes frowned upon and does not optimize well, but it's simple and will get what you want.
declare #searchString varchar(50)
set #searchString = 'Nestle,UFI'
set #searchString = ',' + #searchString + ','
select * from tbl1 where charindex(',' + CompanyName + ',',#SearchString) > 0