split string by " " in a sql query - sql-server

there is a string is "'CNY','THB','USD','VND'" pass from coding.
is there any way that can split it into 'CNY','THB','USD','VND' because I was doing a IN statement. It cannot be done with "'CNY','THB','USD','VND'".

You can use this function, if you are using Microsoft SQL. It will return a table, and in your case you can easily specify if a string in question is in the result set of this table. I am showing you how to use it below
create FUNCTION [dbo].[SPLITSTRING]
(
#CSV varchar(max),
#Delimiter char(1)
)
RETURNS
#Split TABLE (Id int identity(1,1),[OutParam] varchar(max))
AS
BEGIN
Declare #Len as int, #Pos1 int, #Pos2 int
IF LTRIM(RTRIM(#CSV)) = ''
RETURN
SELECT #CSV = #Delimiter + #CSV + #Delimiter
select #Len = len(#csv), #Pos1 = 1
While #Pos1 < #Len
Begin
select #Pos2 = charindex(#Delimiter,#CSV,#Pos1 + 1)
insert #Split select ltrim(rtrim(substring(#csv, #Pos1+1, #Pos2 - #pos1 -1)))
select #Pos1 = #Pos2
End
RETURN
END
Then do
select * from [dbo].[SPLITSTRING]('CNY,THB,USD,VND',',')
What I am doing is creating a table, and splitting out the string between ",", and returning a table.

From above answer , no need of single quote. Pass the string as 'CNY,THB,USD,VND'
in query
Where Varchar_Field IN (select * from [dbo].[SPLITSTRING] ('CNY,THB,USD,VND',','))

Create a Table Vlaued Function
Create FUNCTION [dbo].[Split]
(
#RowData nvarchar(2000),
#SplitOn nvarchar(5)
)
RETURNS #RtnValue table
(
Data nvarchar(100)
)
AS
BEGIN
Declare #Cnt int
Set #Cnt = 1
While (Charindex(#SplitOn,#RowData)>0)
Begin
Insert Into #RtnValue (data)
Select
Data = ltrim(rtrim(Substring(#RowData,1,Charindex(#SplitOn,#RowData)-1)))
Set #RowData = Substring(#RowData,Charindex(#SplitOn,#RowData)+1,len(#RowData))
Set #Cnt = #Cnt + 1
End
Insert Into #RtnValue (data)
Select Data = ltrim(rtrim(#RowData))
Return
END
And Create a table where you want to use split string
Declare #SplitString Table
(
SubjectName nvarchar(50)
)
and Call The Function
INSERT INTO #SplitString SELECT * FROM dbo.Split("Your String",'Character from which you want to split')

Related

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:-

Optimize finding the Nth occurrence of character in string

I wrote a sql server function which returns substring before the Nth occurence of character.
For example,
SELECT dbo.fn_getFirstNthSentence('.', 'hello world.It.is.raining.today', 3)
returns 'hello world.It.Is.' as a result.
The function I wrote looks dirty and slow so I want to optimize it.
Any advice to make it clean is appreciated.
Thank you.
CREATE FUNCTION fn_getFirstNthSentence
(
#TargetStr VARCHAR(MAX) ,
#SearchedStr VARCHAR(8000) ,
#Occurrence INT
)
RETURNS varchar(MAX)
AS
BEGIN
DECLARE #pos INT ,
#counter INT ,
#ret INT;
SET #pos = CHARINDEX(#TargetStr, #SearchedStr);
IF ( #pos = 0 )
RETURN #SearchedStr
SET #counter = 1;
IF #Occurrence = 1
SET #ret = #pos;
ELSE
BEGIN
WHILE ( #counter < #Occurrence )
BEGIN
IF(LEN(#SearchedStr) < #pos + 1)
RETURN #SearchedStr
SELECT #ret = CHARINDEX(#TargetStr, #SearchedStr,
#pos + 1);
IF(#ret = 0)
RETURN #SearchedStr
SET #counter = #counter + 1;
SET #pos = #ret;
END;
END;
RETURN LEFT(#SearchedStr, #ret)
END;
Here is yet another option using a delimited string splitter. The XML method already posted is a good one but this approach does not require a table variable.
This is created as an inline table valued function which should keep the performance really fast.
create function fn_getFirstNthSentence
(
#SearchedStr varchar(100)
, #Occurrence int
, #Delimiter char(1)
) returns table as return
with ParsedValues as
(
select Item
, ItemNumber
from dbo.DelimitedSplit8K(#SearchedStr, #Delimiter)
where ItemNumber <= #Occurrence
)
select top 1 ResultString = STUFF(
(
select #Delimiter + Item
from ParsedValues
order by ItemNumber
for xml path('')), 1,1, '') + #Delimiter
from ParsedValues
This is also using a splitter created by Jeff Moden. It has one feature that none of the other splitter have...a column to indicate which position the value came from. You can find his article an ensuing discussion here. http://www.sqlservercentral.com/articles/Tally+Table/72993/
Then if you want to execute it you can do this quite simply.
declare #String varchar(100) = 'hello world.It.is.raining.today.'
, #Num int = 3
, #Delimiter char(1) = '.'
;
select *
from fn_getFirstNthSentence(#String, #Num, #Delimiter)
If you don't like Jeff Moden's splitter you can find several other options here. http://sqlperformance.com/2012/07/t-sql-queries/split-strings I don't use Moden's for everything but when you need to keep the parsed values in order it is awesome.
--EDIT--
Here is how you could modify this to become a scalar function instead of an inline table valued function. My preference would be to keep the itvf as they are faster and more flexible.
create function fn_getFirstNthSentenceScalar
(
#SearchedStr varchar(100) = 'hello world.It.is.raining.today.this is after 5'
, #Occurrence int = 5
, #Delimiter char(1) = '.'
) returns varchar(max) as begin
declare #RetVal varchar(max);
with ParsedValues as
(
select Item
, ItemNumber
from dbo.DelimitedSplit8K(#SearchedStr, #Delimiter)
where ItemNumber <= #Occurrence
)
select top 1 #RetVal = STUFF(
(
select #Delimiter + Item
from ParsedValues
order by ItemNumber
for xml path('')), 1,1, '') + #Delimiter
from ParsedValues;
return #RetVal
end
--I find these functions to be a mine-field, and at the risk of stepping on a mine I've tried some simplifications - maybe a microscopic improvement in performance
alter FUNCTION fn_getFirstNthSentence
(
#TargetStr VARCHAR(MAX) ,
#SearchedStr VARCHAR(8000) ,
#Occurrence INT
)
RETURNS varchar(MAX)
AS
BEGIN
DECLARE #pos INT ,
#counter INT ;
IF #Occurrence < 1
RETURN NULL;
SELECT #counter = 0, #POS = 1;
WHILE (#counter < #Occurrence AND #POS > 0)
BEGIN
SELECT #POS = CHARINDEX(#TargetStr, #SearchedStr,
#pos + 1);
IF #POS > 0
SET #counter = #counter + 1;
END;
RETURN CASE WHEN #POS > 0 THEN
LEFT(#SearchedStr, #POS)
ELSE
#SearchedStr
END;
END;
Another option is via XML
I can't see your benchmarks, but it is certainly far less code. An added option could be Find the 3rd through 5th occurrence by adding a parameter and changing the Where Seq<=#FindPos to Where Seq Between range1 and range2.
Declare #FindPos int = 3
Declare #String varchar(max) = 'hello world.It.is.raining.today'
Declare #Delim varchar(10) = '.'
Declare #XML xml,#RetVal varchar(max) = ''
Set #XML = Cast('<x>' + Replace(#String,#Delim,'</x><x>')+'</x>' as XML)
Declare #Table table (Seq int identity(1,1),String varchar(max))
Insert Into #Table Select ltrim(rtrim(String.value('.', 'varchar(max)')))+#Delim as value FROM #XML.nodes('x') as T(String)
Select #RetVal=#RetVal + String from #Table Where Seq<=#FindPos Order By Seq
Select #RetVal
Returns
hello world.It.is.
EDIT: If it helps, below is my generic parsing function which returns a
normalized table...
CREATE FUNCTION [dbo].[udf-Str-Parse] (#String varchar(max),#Delimeter varchar(10))
--Usage: Select * from [dbo].[udf-Str-Parse]('Dog,Cat,House,Car',',')
-- Select * from [dbo].[udf-Str-Parse]('John Cappelletti was here',' ')
-- Select * from [dbo].[udf-Str-Parse]('id26,id46|id658,id967','|')
-- Select * from [dbo].[udf-Str-Parse]('hello world. It. is. . raining.today','.')
Returns #ReturnTable Table (Key_PS int IDENTITY(1,1), Key_Value varchar(max))
As
Begin
Declare #XML xml;Set #XML = Cast('<x>' + Replace(#String,#Delimeter,'</x><x>')+'</x>' as XML)
Insert Into #ReturnTable Select Key_Value = ltrim(rtrim(String.value('.', 'varchar(max)'))) FROM #XML.nodes('x') as T(String)
Return
End
So for example:
Select * from [dbo].[udf-Str-Parse]('hello world.It.is.raining.today','.')
Returns
Key_PS Key_Value
1 hello world
2 It
3 is
4 raining
5 today

Multiple-value in one parameter

CREATE FUNCTION [dbo].[func_1]
(
#ListNum AS nvarchar(MAX)
)
RETURNS #t TABLE
(
col_1 nvarchar(MAX)
)
AS
BEGIN
INSERT #t
SELECT col_1
FROM table_name
WHERE col_2 IN (#ListNum)
RETURN
END
When I pass only one value in paramater (for example : 1) the function correctly works but how can I pass multiple value (for example : 1,2,3,4,5). I get the following error :
Procedure execution failed
42000 - [SQL Server]Error converting data type nvarchar to bigint.
Is there a simple way to solve this?
Hi you can try like this,
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
CREATE FUNCTION [dbo].[Func_1] (#ListNum AS NVARCHAR(MAX))
RETURNS #t TABLE (
col_1 NVARCHAR(MAX))
AS
BEGIN
INSERT #t
SELECT p.col1
FROM dbo.Splitstring(#ListNum, ',') s
JOIN Table_Name t
ON t.col2 = s.Item
RETURN
END
DECLARE #var VARCHAR(100)='1,2,3,4'
SELECT *
FROM dbo.Func_1(#var)
Introduce one more function called split string. It will return the comma separated list as a table. Join the comma separated table with your actual table. This will gives the result.
For versions 2008+ using table separated values can assist where the calling procedure can construct the table and you are able to create a table type. If you must pass comma (or other character separated values) in a single string then you will need to separate the comma delimited string into a result set of its own.
The XML method works well when your string doesn't contain any special XML characters such as angle brackets <> - how-to-split-a-comma-separated-value-to-columns
I think this will work for your adjusted function;
CREATE FUNCTION [dbo].[func_1]
(
#ListNum AS nvarchar(MAX)
)
RETURNS #t TABLE
(
col_1 nvarchar(MAX)
)
AS
BEGIN
DECLARE #S varchar(max),
#Split char(1),
#X xml
SELECT #S = #ListNum,
#Split = ','
SELECT #X = CONVERT(xml,' <root> <s>' + REPLACE(#S,#Split,'</s> <s>') + '</s> </root> ')
INSERT #t
SELECT col_1
FROM table_name
WHERE col_2 IN (SELECT [Value] = T.c.value('.','varchar(20)')
FROM #X.nodes('/root/s') T(c))
RETURN
END

Conversion failed when converting the varchar value ',1,2,3' to data type int

This error results when attempting to use a comma delimited parameter in an IN condition
I'm passing a varchar parameter to a stored procedure that looks like this
,1,2,3
And I want to find out if it contains 1 (it doesn't always contain 1)
What's the easiest way to do that in TSQL ?
declare #Nums varchar(max)=',1,2,3'
if 1 in (#Nums) -- conversion error
BEGIN
select * from TestTable
END
You will need to use LIKE to see if the string contains the character 1. Note this will also match 12 or any string with the character '1' in it.
declare #Nums varchar(max)=',1,2,3'
if #Nums LIKE '%1%'
BEGIN
select * from TestTable
END
If you need to match the full number:
CREATE FUNCTION [dbo].[Split_String]
(
#ItemList NVARCHAR(4000),
#delimiter CHAR(1)
)
RETURNS #IDTable TABLE (Item VARCHAR(50))
AS
BEGIN
DECLARE #tempItemList NVARCHAR(4000)
SET #tempItemList = #ItemList
DECLARE #i INT
DECLARE #Item NVARCHAR(4000)
SET #tempItemList = REPLACE (#tempItemList, ' ', '')
SET #i = CHARINDEX(#delimiter, #tempItemList)
WHILE (LEN(#tempItemList) > 0)
BEGIN
IF #i = 0
SET #Item = #tempItemList
ELSE
SET #Item = LEFT(#tempItemList, #i - 1)
INSERT INTO #IDTable(Item) VALUES(#Item)
IF #i = 0
SET #tempItemList = ''
ELSE
SET #tempItemList = RIGHT(#tempItemList, LEN(#tempItemList) - #i)
SET #i = CHARINDEX(#delimiter, #tempItemList)
END
RETURN
END
DECLARE #Nums VARCHAR(MAX) = ',1,2,3'
DECLARE #NumberTable TABLE (item INT)
INSERT INTO #NumberTable
SELECT TRY_CAST(Item AS INT)
FROM dbo.Split_String(#Nums, ',')
IF (SELECT 1 FROM #NumberTable WHERE item = 1) = 1
BEGIN
select * from TestTable
END
You can use CHARINDEX.
declare #Nums varchar(max)=',1,2,3'
IF CHARINDEX(',1,', #Nums+',') > 0
BEGIN
select * from TestTable
END

Why newly created functions can not be found by SQL Server?

I wonder why recently I create functions but SQL Server returns error in calling them, in a case they get added to the functions list of the database.
For example for the following function I get such a error:
declare #cats nvarchar(1000)
set #cats = 'a|b|c|d|e'
SELECT dbo.fncSplit(#Cats, '|')
Error:
Cannot find either column "dbo" or the user-defined function or aggregate "dbo.fncSplit", or the name is ambiguous.
Sample Function:
CREATE FUNCTION [dbo].fncSplit
(
#RowData NVARCHAR(MAX),
#Delimeter NVARCHAR(MAX)
)
RETURNS #RtnValue TABLE
(
ID INT IDENTITY(1,1),
Data NVARCHAR(MAX)
)
AS
BEGIN
DECLARE #Iterator INT
SET #Iterator = 1
DECLARE #FoundIndex INT
SET #FoundIndex = CHARINDEX(#Delimeter,#RowData)
WHILE (#FoundIndex>0)
BEGIN
INSERT INTO #RtnValue (data)
SELECT
Data = LTRIM(RTRIM(SUBSTRING(#RowData, 1, #FoundIndex - 1)))
SET #RowData = SUBSTRING(#RowData,
#FoundIndex + DATALENGTH(#Delimeter) / 2,
LEN(#RowData))
SET #Iterator = #Iterator + 1
SET #FoundIndex = CHARINDEX(#Delimeter, #RowData)
END
INSERT INTO #RtnValue (Data)
SELECT Data = LTRIM(RTRIM(#RowData))
RETURN
END
Any suggestion or solution would be highly appreciated.
This is table-value function. The syntax is
declare #cats nvarchar(1000)
set #cats = 'a|b|c|d|e'
SELECT * from dbo.fncSplit(#Cats, '|')

Resources