How to use the SQL SPLIT function? - sql-server

I wish to use the SQL "split" function:
alter FUNCTION [dbo].[Split3] (#String nvarchar(1000), #Delimiter char(1))
returns #temptable TABLE (items nvarchar(1000))
as
begin
declare #idx int
declare #slice nvarchar(1000)
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(Items) values(#slice)
set #String = right(#String,len(#String) - #idx)
if len(#String) = 0 break
end
return
end
Select * from dbo.Split3 ((Select eqipproc from equipmast where eqcode = 'EQL0000004'),';')
ERROR
Server: Msg 170, Level 15, State 1, Line 1
Line 1: Incorrect syntax near '('.
Server: Msg 170, Level 15, State 1, Line 1
Line 1: Incorrect syntax near ','.

you have to pass CSV string to your split function
DECLARE #result nvarchar(max)
SET #result = ''
SELECT #result = #result + [eqipproc ] + N';'
equipmast where eqcode = 'EQL0000004'
now pass #result to your split function
Select * from dbo.Split3(#result,';')

Try this function
CREATE Function dbo.Str_Split(#string varchar(100),#dl varchar(2))
Returns #outputtbl Table(col varchar(5))
As
BEGIN
Declare #remainingStr varchar(100)=#string
if(CHARINDEX(#dl,#remainingStr,1) = 0)
begin
INSERT INTO #outputtbl
select #remainingStr
end
else
begin
While(CHARINDEX(#dl,#remainingStr,1) > 0)
BEGIN
INSERT INTO #outputtbl
select LEFT(#remainingStr,CHARINDEX(#dl,#remainingStr,1)-1)
SET #remainingStr=RIGHT(#remainingStr,LEN(#remainingStr)-CHARINDEX(#dl,#remainingStr,1))
end
INSERT INTO #outputtbl
select #remainingStr
END
Return
END
--select * from dbo.Str_Split('ab,cd,efg',',')

No matter if filtering by eqcode = 'EQL0000004' returns only one row or more than one, you can execute your [dbo].[Split3] function for each returned row by using CROSS APPLY:
select s.*
from equipmast as e
cross apply dbo.Split3(e.eqipproc , ';') as s
where e.eqcode = 'EQL0000004'
Note: the above solution with CROSS APPLY does the same like the following (which is similar to Utkarsh's answer):
declare #s nvarchar(1000)
select #s = eqipproc from equipmast where eqcode = 'EQL0000004'
select * from dbo.Split3(#s, '.')
The difference is that CROSS APPLY also works if the query returns more than one row.

Related

Create function that returns table doesn't work syntax error? SQL

Hello I need to make a sql request that takes multi parameters in a 'WHERE (abc IN #par1, #par2 ....'
To do so, I found some solutions that use a sql function. I tried to use it without any success.
I'm working on workbench and this is my function code.
CREATE function MultiStringToTable (InStr VARCHAR(255))
RETURNS #TempTab TABLE(
id nvarchar(255) not null
)
AS
BEGIN
-- Ensure input ends with comma
SET #InStr = REPLACE(#InStr + ',', ',,', ',');
DECLARE #SP INT;
DECLARE #VALEUR VARCHAR(1000);
WHILE PATINDEX('%,%', #INSTR ) <> 0
SET #SP = PATINDEX('%,%',#INSTR);
SET #VALEUR = LEFT(#INSTR , #SP - 1);
SET #INSTR = STUFF(#INSTR, 1, #SP, '');
INSERT INTO #TempTab(id) VALUES (#VALEUR);
END;
RETURN;
end;
It causes problem on the '#TempTab'.
Thanks for help.
First - Artem beat me to it but the correct code is:
CREATE function MultiStringToTable (#InStr VARCHAR(255))
RETURNS #TempTab TABLE(id nvarchar(255) not null)
AS
BEGIN
-- Ensure input ends with comma
SET #InStr = REPLACE(#InStr + ',', ',,', ',');
DECLARE #SP INT;
DECLARE #VALEUR VARCHAR(1000);
WHILE PATINDEX('%,%', #INSTR ) <> 0
BEGIN
SET #SP = PATINDEX('%,%',#INSTR);
SET #VALEUR = LEFT(#INSTR , #SP - 1);
SET #INSTR = STUFF(#INSTR, 1, #SP, '');
INSERT INTO #TempTab(id) VALUES (#VALEUR);
END;
RETURN;
END;
GO
That said - ^^^ This is going to be horribly slow. For what you are doing you can use STRING_SPLIT.
DECLARE #instr VARCHAR(8000) = 'abc,,xxx,yyy,,z';
SELECT split.* FROM STRING_SPLIT(#instr,',') AS split WHERE split.[value] > '';
Done. Better yet, you can have any number of commas and they will be treated as one. Note the change below:
DECLARE #instr VARCHAR(8000) = 'abc,,,,xxx,,yyy,,z,w,,,,sss,,';
This returns:
value
----------
abc
xxx
yyy
z
w
sss
To understand the performance difference let's do a quick test.
PRINT CHAR(10)+'STRING_SPLIT'+CHAR(10)+REPLICATE('-',90);
GO
DECLARE #st DATETIME = GETDATE(), #x VARCHAR(8000);
SELECT #x = split.[value]
FROM #strings AS s
CROSS APPLY STRING_SPLIT(s.String,',') AS split
WHERE split.[value] > ''
PRINT DATEDIFF(MS,#st,GETDATE());
GO 3
PRINT CHAR(10)+'dbo.MultiStringToTable'+CHAR(10)+REPLICATE('-',90);
GO
DECLARE #st DATETIME = GETDATE(), #x VARCHAR(8000);
SELECT #x = split.id
FROM #strings AS s
CROSS APPLY dbo.MultiStringToTable(s.String) AS split
WHERE split.id > '';
PRINT DATEDIFF(MS,#st,GETDATE());
GO 3
STRING_SPLIT
--------------------------------------------------------------------------------------
Beginning execution loop
140
184
153
Batch execution completed 3 times.
dbo.MultiStringToTable
--------------------------------------------------------------------------------------
Beginning execution loop
14046
14174
14466
Batch execution completed 3 times.
STRING_SPLIT is about One Hundred times faster and with much less code to boot.
wrong parameter name: #InStr - correct
missed BEGIN keyword after WHILE
correct version:
CREATE function MultiStringToTable (#InStr VARCHAR(255))
RETURNS #TempTab TABLE(
id nvarchar(255) not null
)
AS
BEGIN
-- Ensure input ends with comma
SET #InStr = REPLACE(#InStr + ',', ',,', ',');
DECLARE #SP INT;
DECLARE #VALEUR VARCHAR(1000);
WHILE PATINDEX('%,%', #INSTR ) <> 0
BEGIN
SET #SP = PATINDEX('%,%',#INSTR);
SET #VALEUR = LEFT(#INSTR , #SP - 1);
SET #INSTR = STUFF(#INSTR, 1, #SP, '');
INSERT INTO #TempTab(id) VALUES (#VALEUR);
END;
RETURN;
end;

How to get split string with quotation on each split item in SQL Server?

How to get split string with quotation on each split item in SQL Server? I have tried this
declare #departmentNames as varchar(max) = 'Account, hod'
--declare #departmentNames as varchar(max) = 'Account'+', '+'hod'
print #departmentNames
I get result like this => Account,hod
but I want it like this => 'Account', 'hod'
so that I could use it in
select *
from tblDepartment
where name in (select item from splitString(#departmentNames, ','))
I know if I use integers with id column it will work fine i.e => 1, 2, 3, 4 but I want to try it with strings.
So is there anyone who can help me with this?
You can use apply :
select td.*
from tblDepartment td cross apply
<shema>.splitString(#departmentNames, ',') spt(item) -- add schema name
where spt.item = td.name;
If you want string comparison, you can do concatenation.
Note : use Schema name while calling UDF function.
First create this function:
CREATE FUNCTION [dbo].[fn_Split]
(#String varchar(8000),
#Delimiter varchar(50))
RETURNS #temptable TABLE (items varchar(8000))
AS
BEGIN
/*
SELECT * FROM dbo.fn_Split('12345;thome', ';')
*/
DECLARE #idx int
DECLARE #slice varchar(8000)
DECLARE #delimiterLength int
SET #delimiterLength = len(#Delimiter)
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(Items)
VALUES (LTRIM(RTRIM(#slice)))
SET #String = RIGHT(#String, LEN(#String) - #idx - #delimiterLength + 1)
IF LEN (#String) = 0
BREAK
END
RETURN
END
After creating this function then you can test with below query.
It splits words with any delimiter you are passing
select items from dbo.fn_Split('ACCOUNT ,HOD',',')
select items from dbo.fn_Split('ACCOUNT ; HOD',';')
Then pass variable and and use join with this function.
Use table alias for easy understanding
declare #departmentNames as varchar(max) = ('Account, hod')
select t.*
from tblDepartment t
inner join
(Select items
from dbo.fn_Split (#departmentNames, ',')) A on t.name = A.items
I create temptable for testing and this query will return output like below

How I can split the directory path to the 5 level in SQL server?

I need to parse directory to 5 Level in SQL server with string function.
\ABC\BC\ADS\ADS\123\456\123\ABD
\ABC\BC\ADS\ADS\156\456\123\ABD\123\565
\ABC\BC\ADS\ADS\179\456\123\ABD\123\565\ZYX
I need a below result.
\ABC\BC\ADS\ADS\123\
\ABC\BC\ADS\ADS\156\
\ABC\BC\ADS\ADS\179\
If you are allowed to create a database function, you can achieve your required data.
Create Function:
USE [your_database_name];
GO
ALTER FUNCTION [dbo].[find_nth_char_position]
(#TargetStr VARCHAR(8000),
#SearchedStr VARCHAR(8000),
#Occurrence INT
)
RETURNS INT
AS
BEGIN
DECLARE #pos INT, #counter INT, #ret INT;
SET #pos = CHARINDEX(#TargetStr, #SearchedStr);
SET #counter = 1;
IF #Occurrence = 1
SET #ret = #pos;
ELSE
BEGIN
WHILE(#counter < #Occurrence)
BEGIN
IF CHARINDEX(#TargetStr, #SearchedStr, #pos + 1) = 0
BEGIN
RETURN(0)
END
ELSE
BEGIN
SELECT #ret = CHARINDEX(#TargetStr, #SearchedStr, #pos + 1);
SET #counter = #counter + 1;
SET #pos = #ret;
END
END;
END;
RETURN(#ret);
END;
GO
This above function will return the Nth position of a character in the provided string. As you required the PATH till level 5, I have find the 6th position of character "\" and keep string up to that position using SUBSTRING method.
Select Statement:
SELECT
SUBSTRING
(
<db column name>,
1,
CASE
WHEN [dbo].[find_nth_char_position]('\',<db column name>,6) = 0 THEN LEN(<db column name>)
ELSE [dbo].[find_nth_char_position]('\',<db column name>,6)
END
)
FROM <your_table>
DEMO HERE
You may try with the following approach:
Table:
CREATE TABLE #Data (
TextData varchar(1000)
)
INSERT INTO #Data
(TextData)
VALUES
('\ABC\BC\ADS\ADS\123\456\123\ABD'),
('\ABC\BC\ADS\ADS\156\456\123\ABD\123\565'),
('\ABC\BC\ADS\ADS'),
('\ABC\BC\ADS\ADS\179\456\123\ABD\123\565\ZYX')
Statement:
SELECT CONCAT(Part1, Part2, Part3, Part4, Part5, Part6) AS TextData
FROM #Data d
CROSS APPLY (SELECT LEFT(CONCAT(d.TextData, '\'), CHARINDEX('\', CONCAT(d.TextData, '\'))) AS Part1, STUFF(CONCAT(d.TextData, '\'), 1, CHARINDEX('\', CONCAT(d.TextData, '\')), '') AS TextData) c1
CROSS APPLY (SELECT LEFT(c1.TextData, CHARINDEX('\', c1.TextData)) AS Part2, STUFF(c1.TextData, 1, CHARINDEX('\', c1.TextData), '') AS TextData) c2
CROSS APPLY (SELECT LEFT(c2.TextData, CHARINDEX('\', c2.TextData)) AS Part3, STUFF(c2.TextData, 1, CHARINDEX('\', c2.TextData), '') AS TextData) c3
CROSS APPLY (SELECT LEFT(c3.TextData, CHARINDEX('\', c3.TextData)) AS Part4, STUFF(c3.TextData, 1, CHARINDEX('\', c3.TextData), '') AS TextData) c4
CROSS APPLY (SELECT LEFT(c4.TextData, CHARINDEX('\', c4.TextData)) AS Part5, STUFF(c4.TextData, 1, CHARINDEX('\', c4.TextData), '') AS TextData) c5
CROSS APPLY (SELECT LEFT(c5.TextData, CHARINDEX('\', c5.TextData)) AS Part6, STUFF(c5.TextData, 1, CHARINDEX('\', c5.TextData), '') AS TextData) c6
Output:
--------------------
TextData
--------------------
\ABC\BC\ADS\ADS\123\
\ABC\BC\ADS\ADS\156\
\ABC\BC\ADS\ADS\
\ABC\BC\ADS\ADS\179\
You may try this. By using string_split you can convert your string into rows, then find out the top records as you needed in your code to get the desired result.
After that use stuff to again concatenate into single string and get your output.
declare #tab table ( col varchar(max) )
insert into #tab ( col)
values ( '\ABC\BC\ADS\ADS\123\456\123\ABD' )
,('\ABC\BC\ADS\ADS\156\456\123\ABD\123\565')
,('\ABC\BC\ADS\ADS\179\456\123\ABD\123\565\ZYX')
select stuff((select top 6 '\' + value from string_split(col, '\') for xml path('')),1,1,'') as Result from #tab
--- by using our own created split function, if you are allowed to do so.
select stuff((select top 6 '\' + items from Split(col, '\') for xml path('')),1,1,'') as Result from #tab
Result
----------------
\ABC\BC\ADS\ADS\123
\ABC\BC\ADS\ADS\156
\ABC\BC\ADS\ADS\179
You may create your own split function to make sure that order will not change in parsing.
GO
CREATE FUNCTION [dbo].[Split]
(#String nvarchar(max), #Delimiter char(1))
RETURNS #Results TABLE (Items varchar(200))
AS
BEGIN
DECLARE #INDEX INT
DECLARE #SLICE nvarchar(max)
-- HAVE TO SET TO 1 SO IT DOESNT EQUAL Z
-- ERO FIRST TIME IN LOOP
SELECT #INDEX = 1
WHILE #INDEX !=0
BEGIN
-- GET THE INDEX OF THE FIRST OCCURENCE OF THE SPLIT CHARACTER
SELECT #INDEX = CHARINDEX(#Delimiter,#STRING)
-- NOW PUSH EVERYTHING TO THE LEFT OF IT INTO THE SLICE VARIABLE
IF #INDEX !=0
SELECT #SLICE = LEFT(#STRING,#INDEX - 1)
ELSE
SELECT #SLICE = #STRING
-- PUT THE ITEM INTO THE RESULTS SET
INSERT INTO #Results(Items) VALUES(#SLICE)
-- CHOP THE ITEM REMOVED OFF THE MAIN STRING
SELECT #STRING = RIGHT(#STRING,LEN(#STRING) - #INDEX)
-- BREAK OUT IF WE ARE DONE
IF LEN(#STRING) = 0 BREAK
END
RETURN
END
GO

Remove duplicates from String using sql

If I have a value something like '10,10,20,30,40,20' in a field of table, then I want to make it as '10,20,30,40'
Is there any sql function to do such thing?
Thanks
Sudhakar
using Jeff's DelimitedSplit8K from http://www.sqlservercentral.com/articles/Tally+Table/72993/
declare #value varchar(100) = '10,10,20,30,40,20',
#new_value varchar(100)
select #new_value = isnull(#new_value + ',', '') + Item
from DelimitedSplit8K(#value, ',')
group by Item
order by Item
select #new_value
Did this long ago. This might need some modifications. But it generates output.
Try :
DECLARE #Data_String AS VARCHAR(1000), #Result as varchar(1000)=''
SET #Data_String = '10,10,20,30,40,20'
SET #Data_String = REPLACE(#Data_String,'|',',')
select #Data_String;
SELECT #Result=#Result+col+',' from(
SELECT DISTINCT t.c.value('.','varchar(100)') col from(
SELECT cast('<A>'+replace(#Data_String,',','</A><A>')+'</A>' as xml)col1)data
cross apply col1.nodes('/A') as t(c))Data
SELECT LEFT(#Result,LEN(#Result)-1)
believing it stores integer number you can get them with creating a function first you need to split the values then have to use a distinct function as below
1st create a function like
CREATE FUNCTION [dbo].[idpGetSplitedString]
(
#String varchar(8000),
#Delimiter char(1)
)
RETURNS
#temptable TABLE
(
items varchar(8000)
)
AS
BEGIN
declare #idx int
declare #slice varchar(8000)
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(Items) values(rtrim(ltrim(#slice)))
set #String = right(#String,len(#String) - #idx)
if len(#String) = 0 break
end
RETURN
END
then call the function like
select [dbo].idpGetSplitedString as Values

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