SQL Stored Procedure LIKE - sql

This is a simple question and I can't seem to think of a solution.
I have this defined in my stored procedure:
#communityDesc varchar(255) = NULL
#communityDesc is "aaa,bbb,ccc"
and in my actual query I am trying to use IN
WHERE AREA IN (#communityDesc)
but this will not work because my commas are inside the string instead of like this "aaa", "bbb", "ccc"
So my question is, is there anything I can do to #communityDesc so it will work with my IN statement, like reformat the string?

This article could help you by your problem:
http://sqlperformance.com/2012/07/t-sql-queries/split-strings
In this article Aaron Bertrand is writing about your problem.
It's really long and very detailed.
One Way would be this:
CREATE FUNCTION dbo.SplitStrings_XML
(
#List NVARCHAR(MAX),
#Delimiter NVARCHAR(255)
)
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN
(
SELECT Item = y.i.value('(./text())[1]', 'nvarchar(4000)')
FROM
(
SELECT x = CONVERT(XML, '<i>'
+ REPLACE(#List, #Delimiter, '</i><i>')
+ '</i>').query('.')
) AS a CROSS APPLY x.nodes('i') AS y(i)
);
GO
With this function you only call:
WHERE AREA IN (SELECT Item FROM dbo.SplitStrings_XML(#communityDesc, N','))
Hope this could help you.

The simplest way to use this variable is:
SELECT *
FROM something
WHERE ',' + #communityDesc + ',' Like '%,' + AREA + ',%'
this is for tsql, for oracle use || to concatenate strings

In only works with sets of values, not with characters in a string. To answer your question technically, the only way you could do this is to create a set of values representing the three values 'aaa', 'bbb' & 'ccc' and then put those three values into a table (a Temp Table or table variable (in SQL Server), and then perform IN against that set of values (against the table:
declare #Vals table (value varchar(20))
insert #vals(Value) Values('aaa')
insert #vals(Value) Values('bbb')
insert #vals(Value) Values('ccc')
select * from SomeOtherTable
Where SomeColumn IN (Select value from #vals)
To create the set you would need to create an empty temp table or table variable to hold this set of values, parse the comma delimited string into individual values, and enter those individual values into the temp table or table variable.
although you don't say, if you are using SQL Server, the following is a SQL Server User Defined function (UDF) that will parse a delimited string and return a table with one row for each delimted value:
if you create the UDF, then you would use it as follows:
select * from SomeOtherTable
Where SomeColumn IN
(Select sVal from
dbo.ParseSTring(#communityDesc, ','))
/****** Object: UserDefinedFunction [dbo].[ParseString]
Script Date: 4/8/2016 1:53:00 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER FUNCTION [dbo].[ParseString] (#S Text, #delim VarChar(5))
Returns #tOut Table
(ValNum Integer Identity Primary Key,
sVal VarChar(8000))
As
Begin
Declare #dLLen TinyInt -- Length of delimiter
Declare #sWin VarChar(8000)-- Will Contain Window into text string
Declare #wLen Integer -- Length of Window
Declare #wLast TinyInt -- Boolean to indicate processing Last Window
Declare #wPos Integer -- Start Position of Window within Text String
Declare #sVal VarChar(8000)-- String Data to insert into output Table
Declare #BtchSiz Integer -- Maximum Size of Window
Set #BtchSiz = 7900 -- (Reset to smaller values to test routine)
Declare #dPos Integer -- Position within Window of next Delimiter
Declare #Strt Integer -- Start Position of each data value in Window
-- --------------------------------------------------------------
-- ---------------------------
If #delim is Null Set #delim = '|'
If DataLength(#S) = 0 Or
Substring(#S, 1, #BtchSiz) = #delim Return
-- ---------------------------
Select #dLLen = Len(#delim),
#Strt = 1, #wPos = 1,
#sWin = Substring(#S, 1, #BtchSiz)
Select #wLen = Len(#sWin),
#wLast = Case When Len(#sWin) = #BtchSiz
Then 0 Else 1 End,
#dPos = CharIndex(#delim, #sWin, #Strt)
-- ----------------------------
While #Strt <= #wLen
Begin
If #dPos = 0 Begin -- No More delimiters in window
If #wLast = 1 Set #dPos = #wLen + 1
Else Begin
Set #wPos = #wPos + #Strt - 1
Set #sWin = Substring(#S, #wPos, #BtchSiz)
-- -------------------------------------
Select #wLen = Len(#sWin), #Strt = 1,
#wLast = Case When Len(#sWin) = #BtchSiz
Then 0 Else 1 End,
#dPos = CharIndex(#delim, #sWin, 1)
If #dPos = 0 Set #dPos = #wLen + 1
End
End
-- -------------------------------
Set #sVal = LTrim(Substring(#sWin, #Strt, #dPos - #Strt))
Insert #tOut (sVal) Values (#sVal)
-- -------------------------------
-- Move #Strt to char after last delimiter
Set #Strt = #dPos + #dLLen
Set #dPos = CharIndex(#delim, #sWin, #Strt)
End
Return
End

at first you most create a function to split string some thing like this code
CREATE FUNCTION dbo.splitstring ( #stringToSplit VARCHAR(MAX) )
RETURNS
#returnList TABLE ([Name] [nvarchar] (500))
AS
BEGIN
DECLARE #name NVARCHAR(255)
DECLARE #pos INT
WHILE CHARINDEX(',', #stringToSplit) > 0
BEGIN
SELECT #pos = CHARINDEX(',', #stringToSplit)
SELECT #name = SUBSTRING(#stringToSplit, 1, #pos-1)
INSERT INTO #returnList
SELECT #name
SELECT #stringToSplit = SUBSTRING(#stringToSplit, #pos+1, LEN(#stringToSplit)-#pos)
END
INSERT INTO #returnList
SELECT #stringToSplit
RETURN
END
then you can use this function is your query like this
WHERE AREA IN (dbo.splitstring(#communityDesc))

You can do this by splitting your string using a split function provided here. The function returns a table having a single column which holds your tokens (i.e. 'aaa', 'bbb' ...).
Your query should look like this:
-- get the splits
SELECT Name INTO #someTemp
FROM dbo.splitstring(#communityDesc)
-- get data where area in within description
SELECT 1
FROM yourTable T
WHERE EXISTS (SELECT 1 FROM #someTemp tmp WHERE T.Area = tmp.Name)

A different approach is to use CHARINDEX(). However, using a function in a WHERE clause will slow down the performance.
WHERE CHARINDEX(','+area+',',','+#CommunityDec+',')> 0
If you area field is always 3 letters, you can simplify this.
WHERE CHARINDEX(area,#CommunityDec)> 0
This is a quick solution, but also a stop gap. A better solution is to change the string lookup approach to build a table with one row per search criteria and using a JOIN or sub query.

You can simply split this csv using XML and use this to filter in your query. No need to use a User defined function or #Table_Valiable or #Temp_Table here.
DECLARE #xml as xml,#communityDesc varchar(255) = 'aaa,bbb,ccc'
SET #xml = cast(('<X>'+replace(#communityDesc,',' ,'</X><X>')+'</X>') as xml)
SELECT * FROM TABLE1
WHERE AREA IN (
SELECT N.value('.', 'varchar(10)') as value FROM #xml.nodes('X') as T(N)
)
If you required this split values in further process, then you can Insert this to a #table_Variable or #Temp_Table and use them.

Related

Query stuff result left join on multiple ids [duplicate]

Duplicate of
Dynamic SQL Comma Delimited Value Query
Parameterized Queries with Like and In
I have a SQL Server Stored Procedure where I would like to pass a varchar full of comma delimited values to an IN function. For example:
DECLARE #Ids varchar(50);
SET #Ids = '1,2,3,5,4,6,7,98,234';
SELECT *
FROM sometable
WHERE tableid IN (#Ids);
This does not work of course. I get the error:
Conversion failed when converting the varchar value '1,2,3,5,4,6,7,98,234' to data type int.
How can I accomplish this (or something relatively similar) without resorting to building dynamic SQL?
Of course if you're lazy like me, you could just do this:
Declare #Ids varchar(50) Set #Ids = ',1,2,3,5,4,6,7,98,234,'
Select * from sometable
where Charindex(','+cast(tableid as varchar(8000))+',', #Ids) > 0
Don't use a function that loops to split a string!, my function below will split a string very fast, with no looping!
Before you use my function, you need to set up a "helper" table, you only need to do this one time per database:
CREATE TABLE Numbers
(Number int NOT NULL,
CONSTRAINT PK_Numbers PRIMARY KEY CLUSTERED (Number ASC)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
DECLARE #x int
SET #x=0
WHILE #x<8000
BEGIN
SET #x=#x+1
INSERT INTO Numbers VALUES (#x)
END
use this function to split your string, which does not loop and is very fast:
CREATE FUNCTION [dbo].[FN_ListToTable]
(
#SplitOn char(1) --REQUIRED, the character to split the #List string on
,#List varchar(8000) --REQUIRED, the list to split apart
)
RETURNS
#ParsedList table
(
ListValue varchar(500)
)
AS
BEGIN
/**
Takes the given #List string and splits it apart based on the given #SplitOn character.
A table is returned, one row per split item, with a column name "ListValue".
This function workes for fixed or variable lenght items.
Empty and null items will not be included in the results set.
Returns a table, one row per item in the list, with a column name "ListValue"
EXAMPLE:
----------
SELECT * FROM dbo.FN_ListToTable(',','1,12,123,1234,54321,6,A,*,|||,,,,B')
returns:
ListValue
-----------
1
12
123
1234
54321
6
A
*
|||
B
(10 row(s) affected)
**/
----------------
--SINGLE QUERY-- --this will not return empty rows
----------------
INSERT INTO #ParsedList
(ListValue)
SELECT
ListValue
FROM (SELECT
LTRIM(RTRIM(SUBSTRING(List2, number+1, CHARINDEX(#SplitOn, List2, number+1)-number - 1))) AS ListValue
FROM (
SELECT #SplitOn + #List + #SplitOn AS List2
) AS dt
INNER JOIN Numbers n ON n.Number < LEN(dt.List2)
WHERE SUBSTRING(List2, number, 1) = #SplitOn
) dt2
WHERE ListValue IS NOT NULL AND ListValue!=''
RETURN
END --Function FN_ListToTable
you can use this function as a table in a join:
SELECT
Col1, COl2, Col3...
FROM YourTable
INNER JOIN FN_ListToTable(',',#YourString) s ON YourTable.ID = s.ListValue
Here is your example:
Select * from sometable where tableid in(SELECT ListValue FROM dbo.FN_ListToTable(',',#Ids) s)
No Table No Function No Loop
Building on the idea of parsing your list into a table our DBA suggested using XML.
Declare #Ids varchar(50)
Set #Ids = ‘1,2,3,5,4,6,7,98,234’
DECLARE #XML XML
SET #XML = CAST('<i>' + REPLACE(#Ids, ',', '</i><i>') + '</i>' AS XML)
SELECT *
FROM
SomeTable
INNER JOIN #XML.nodes('i') x(i)
ON SomeTable .Id = x.i.value('.', 'VARCHAR(MAX)')
These seems to have the same performance as #KM's answer but, I think, a lot simpler.
You can create a function that returns a table.
so your statement would be something like
select * from someable
join Splitfunction(#ids) as splits on sometable.id = splits.id
Here is a simular function.
CREATE FUNCTION [dbo].[FUNC_SplitOrderIDs]
(
#OrderList varchar(500)
)
RETURNS
#ParsedList table
(
OrderID int
)
AS
BEGIN
DECLARE #OrderID varchar(10), #Pos int
SET #OrderList = LTRIM(RTRIM(#OrderList))+ ','
SET #Pos = CHARINDEX(',', #OrderList, 1)
IF REPLACE(#OrderList, ',', '') <> ''
BEGIN
WHILE #Pos > 0
BEGIN
SET #OrderID = LTRIM(RTRIM(LEFT(#OrderList, #Pos - 1)))
IF #OrderID <> ''
BEGIN
INSERT INTO #ParsedList (OrderID)
VALUES (CAST(#OrderID AS int)) --Use Appropriate conversion
END
SET #OrderList = RIGHT(#OrderList, LEN(#OrderList) - #Pos)
SET #Pos = CHARINDEX(',', #OrderList, 1)
END
END
RETURN
END
It's a very common question. Canned answer, several nice techniques:
http://www.sommarskog.se/arrays-in-sql-2005.html
This works perfectly! The below answers are too complicated. Don't look at this as dynamic. Set up your store procedure as follows:
(#id as varchar(50))
as
Declare #query as nvarchar(max)
set #query ='
select * from table
where id in('+#id+')'
EXECUTE sp_executesql #query
I can suggest using WITH like this:
DECLARE #Delim char(1) = ',';
SET #Ids = #Ids + #Delim;
WITH CTE(i, ls, id) AS (
SELECT 1, CHARINDEX(#Delim, #Ids, 1), SUBSTRING(#Ids, 1, CHARINDEX(#Delim, #Ids, 1) - 1)
UNION ALL
SELECT i + 1, CHARINDEX(#Delim, #Ids, ls + 1), SUBSTRING(#Ids, ls + 1, CHARINDEX(#Delim, #Ids, ls + 1) - CHARINDEX(#Delim, #Ids, ls) - 1)
FROM CTE
WHERE CHARINDEX(#Delim, #Ids, ls + 1) > 1
)
SELECT t.*
FROM yourTable t
INNER JOIN
CTE c
ON t.id = c.id;
I think a very simple solution could be following:
DECLARE #Ids varchar(50);
SET #Ids = '1,2,3,5,4,6,7,98,234';
SELECT *
FROM sometable
WHERE ','+#Ids+',' LIKE '%,'+CONVERT(VARCHAR(50),tableid)+',%';
Without using dynamic SQL, you have to take the input variable and use a split function to put the data into a temp table and then join to that.
Thanks, for your function I Used IT........................
This is my EXAMPLE
**UPDATE [RD].[PurchaseOrderHeader]
SET [DispatchCycleNumber] ='10'
WHERE OrderNumber in(select * FROM XA.fn_SplitOrderIDs(#InvoiceNumberList))**
CREATE FUNCTION [XA].[fn_SplitOrderIDs]
(
#OrderList varchar(500)
)
RETURNS
#ParsedList table
(
OrderID int
)
AS
BEGIN
DECLARE #OrderID varchar(10), #Pos int
SET #OrderList = LTRIM(RTRIM(#OrderList))+ ','
SET #Pos = CHARINDEX(',', #OrderList, 1)
IF REPLACE(#OrderList, ',', '') <> ''
BEGIN
WHILE #Pos > 0
BEGIN
SET #OrderID = LTRIM(RTRIM(LEFT(#OrderList, #Pos - 1)))
IF #OrderID <> ''
BEGIN
INSERT INTO #ParsedList (OrderID)
VALUES (CAST(#OrderID AS int)) --Use Appropriate conversion
END
SET #OrderList = RIGHT(#OrderList, LEN(#OrderList) - #Pos)
SET #Pos = CHARINDEX(',', #OrderList, 1)
END
END
RETURN
END
If you use SQL Server 2008 or higher, use table valued parameters; for example:
CREATE PROCEDURE [dbo].[GetAccounts](#accountIds nvarchar)
AS
BEGIN
SELECT *
FROM accountsTable
WHERE accountId IN (select * from #accountIds)
END
CREATE TYPE intListTableType AS TABLE (n int NOT NULL)
DECLARE #tvp intListTableType
-- inserts each id to one row in the tvp table
INSERT #tvp(n) VALUES (16509),(16685),(46173),(42925),(46167),(5511)
EXEC GetAccounts #tvp
Its been a while but I have done this in the past using XML as a interim.
I can't take any credit for this, but I'm afraid I no longer know where I got this idea from:
-- declare the variables needed
DECLARE #xml as xml,#str as varchar(100),#delimiter as varchar(10)
-- The string you want to split
SET #str='A,B,C,D,E,Bert,Ernie,1,2,3,4,5'
-- What you want to split on. Can be a single character or a string
SET #delimiter =','
-- Convert it to an XML document
SET #xml = cast(('<X>'+replace(#str,#delimiter ,'</X><X>')+'</X>') as xml)
-- Select back from the XML
SELECT N.value('.', 'varchar(10)') as value FROM #xml.nodes('X') as T(N)
Create a table function like below which parse comma separated varchar and returns a table that can be inner joined with other tables.
CREATE FUNCTION [dbo].[fn_SplitList]
(
#inString varchar(MAX) = '',
#inDelimiter char(1) = ',' -- Keep the delimiter to 100 chars or less. Generally a delimiter will be 1-2 chars only.
)
RETURNS #tbl_Return table
(
Unit varchar(1000) COLLATE Latin1_General_BIN
)
AS
BEGIN
INSERT INTO #tbl_Return
SELECT DISTINCT
LTRIM(RTRIM(piece.value('./text()[1]', 'varchar(1000)'))) COLLATE DATABASE_DEFAULT AS Unit
FROM
(
--
-- Replace any delimiters in the string with the "X" tag.
--
SELECT
CAST(('<X>' + REPLACE(s0.prsString, s0.prsSplitDelimit, '</X><X>') + '</X>') AS xml).query('.') AS units
FROM
(
--
-- Convert the string and delimiter into XML.
--
SELECT
(SELECT #inString FOR XML PATH('')) AS prsString,
(SELECT #inDelimiter FOR XML PATH('')) AS prsSplitDelimit
) AS s0
) AS s1
CROSS APPLY units.nodes('X') x(piece)
RETURN
END
=================================================
Now consume above created table function in your code,creation of function is one time activity in your database that can be used across databases as well on same server.
DECLARE #Ids varchar(50);
SET #Ids = '1,2,3,5,4,6,7,98,234';
SELECT
*
FROM sometable AS st
INNER JOIN fn_SplitList(#ids, ',') AS sl
ON sl.unit = st.tableid
I've written a stored procedure to show how to do this before.
You basically have to process the string.
I tried to post the code here but the formatting got all screwy.
IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = object_id(N'[dbo].[uspSplitTextList]') AND OBJECTPROPERTY(id, N'IsProcedure') = 1)
DROP PROCEDURE [dbo].[uspSplitTextList]
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_NULLS ON
GO
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
-- uspSplitTextList
--
-- Description:
-- splits a separated list of text items and returns the text items
--
-- Arguments:
-- #list_text - list of text items
-- #Delimiter - delimiter
--
-- Notes:
-- 02/22/2006 - WSR : use DATALENGTH instead of LEN throughout because LEN doesn't count trailing blanks
--
-- History:
-- 02/22/2006 - WSR : revised algorithm to account for items crossing 8000 character boundary
-- 09/18/2006 - WSR : added to this project
--
CREATE PROCEDURE uspSplitTextList
#list_text text,
#Delimiter varchar(3)
AS
SET NOCOUNT ON
DECLARE #InputLen integer -- input text length
DECLARE #TextPos integer -- current position within input text
DECLARE #Chunk varchar(8000) -- chunk within input text
DECLARE #ChunkPos integer -- current position within chunk
DECLARE #DelimPos integer -- position of delimiter
DECLARE #ChunkLen integer -- chunk length
DECLARE #DelimLen integer -- delimiter length
DECLARE #ItemBegPos integer -- item starting position in text
DECLARE #ItemOrder integer -- item order in list
DECLARE #DelimChar varchar(1) -- first character of delimiter (simple delimiter)
-- create table to hold list items
-- actually their positions because we may want to scrub this list eliminating bad entries before substring is applied
CREATE TABLE #list_items ( item_order integer, item_begpos integer, item_endpos integer )
-- process list
IF #list_text IS NOT NULL
BEGIN
-- initialize
SET #InputLen = DATALENGTH(#list_text)
SET #TextPos = 1
SET #DelimChar = SUBSTRING(#Delimiter, 1, 1)
SET #DelimLen = DATALENGTH(#Delimiter)
SET #ItemBegPos = 1
SET #ItemOrder = 1
SET #ChunkLen = 1
-- cycle through input processing chunks
WHILE #TextPos <= #InputLen AND #ChunkLen <> 0
BEGIN
-- get current chunk
SET #Chunk = SUBSTRING(#list_text, #TextPos, 8000)
-- setup initial variable values
SET #ChunkPos = 1
SET #ChunkLen = DATALENGTH(#Chunk)
SET #DelimPos = CHARINDEX(#DelimChar, #Chunk, #ChunkPos)
-- loop over the chunk, until the last delimiter
WHILE #ChunkPos <= #ChunkLen AND #DelimPos <> 0
BEGIN
-- see if this is a full delimiter
IF SUBSTRING(#list_text, (#TextPos + #DelimPos - 1), #DelimLen) = #Delimiter
BEGIN
-- insert position
INSERT INTO #list_items (item_order, item_begpos, item_endpos)
VALUES (#ItemOrder, #ItemBegPos, (#TextPos + #DelimPos - 1) - 1)
-- adjust positions
SET #ItemOrder = #ItemOrder + 1
SET #ItemBegPos = (#TextPos + #DelimPos - 1) + #DelimLen
SET #ChunkPos = #DelimPos + #DelimLen
END
ELSE
BEGIN
-- adjust positions
SET #ChunkPos = #DelimPos + 1
END
-- find next delimiter
SET #DelimPos = CHARINDEX(#DelimChar, #Chunk, #ChunkPos)
END
-- adjust positions
SET #TextPos = #TextPos + #ChunkLen
END
-- handle last item
IF #ItemBegPos <= #InputLen
BEGIN
-- insert position
INSERT INTO #list_items (item_order, item_begpos, item_endpos)
VALUES (#ItemOrder, #ItemBegPos, #InputLen)
END
-- delete the bad items
DELETE FROM #list_items
WHERE item_endpos < item_begpos
-- return list items
SELECT SUBSTRING(#list_text, item_begpos, (item_endpos - item_begpos + 1)) AS item_text, item_order, item_begpos, item_endpos
FROM #list_items
ORDER BY item_order
END
DROP TABLE #list_items
RETURN
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
GO
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO
I have same idea with user KM. but do not need extra table Number. Just this function only.
CREATE FUNCTION [dbo].[FN_ListToTable]
(
#SplitOn char(1) --REQUIRED, the character to split the #List string on
,#List varchar(8000) --REQUIRED, the list to split apart
)
RETURNS
#ParsedList table
(
ListValue varchar(500)
)
AS
BEGIN
DECLARE #number int = 0
DECLARE #childString varchar(502) = ''
DECLARE #lengthChildString int = 0
DECLARE #processString varchar(502) = #SplitOn + #List + #SplitOn
WHILE #number < LEN(#processString)
BEGIN
SET #number = #number + 1
SET #lengthChildString = CHARINDEX(#SplitOn, #processString, #number + 1) - #number - 1
IF #lengthChildString > 0
BEGIN
SET #childString = LTRIM(RTRIM(SUBSTRING(#processString, #number + 1, #lengthChildString)))
IF #childString IS NOT NULL AND #childString != ''
BEGIN
INSERT INTO #ParsedList(ListValue) VALUES (#childString)
SET #number = #number + #lengthChildString - 1
END
END
END
RETURN
END
And here is the test:
SELECT ListValue FROM dbo.FN_ListToTable('/','a/////bb/c')
Result:
ListValue
______________________
a
bb
c
-- select * from dbo.Split_ID('77,106')
ALTER FUNCTION dbo.Split_ID(#String varchar(8000))
returns #temptable TABLE (ID varchar(8000))
as
begin
declare #idx int
declare #slice varchar(8000)
declare #Delimiter char(1)
set #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(ID) values(#slice)
set #String = right(#String,len(#String) - #idx)
if len(#String) = 0 break
end
return
end
You could do it like this:
create or replace
PROCEDURE UDP_SETBOOKMARK
(
P_USERID IN VARCHAR2
, P_BOOKMARK IN VARCHAR2
) AS
BEGIN
UPDATE T_ER_Bewertung
SET LESEZEICHEN = P_BOOKMARK
WHERE STAMM_ID in( select regexp_substr(P_USERID,'[^,]+', 1, level) from dual
connect by regexp_substr(P_USERID, '[^,]+', 1, level) is not null )
and ER_ID = (select max(ER_ID) from T_ER_Bewertung_Kopie);
commit;
END UDP_SETBOOKMARK;
Then try it with
Begin
UDP_SETBOOKMARK ('1,2,3,4,5', 'Test');
End;
You can use this IN-Clause with regexp_substr in other situations too, just try it.
Error 493: The column 'i' that was returned from the nodes() method cannot be
used directly. It can only be used with one of the four XML data type
methods, exist(), nodes(), query(), and value(), or in IS NULL and IS NOT
NULL checks.
The above errorr was fixed in SQL Server 2014 by using following snippet
Declare #Ids varchar(50)
Set #Ids = '1,2,3,5,4,6,7,98,234'
DECLARE #XML XML
SET #XML = CAST('<i>' + REPLACE(#Ids, ',', '</i><i>') + '</i>' AS XML)
SELECT SomeTable.*
FROM
SomeTable
cross apply #XML.nodes('i') x(i)
where SomeTable .Id = x.i.value('.', 'VARCHAR(MAX)')
Try this:
SELECT ProductId, Name, Tags
FROM Product
WHERE '1,2,3,' LIKE '%' + CAST(ProductId AS VARCHAR(20)) + ',%';
As said on the last example of this link
Tons of answers here, but to add my two cents I think STRING_SPLIT is a very simple approach to this sort of problem:
DECLARE #Ids varchar(50);
SET #Ids = '1,2,3,5,4,6,7,98,234';
SELECT *
FROM sometable
WHERE tableid IN;
(SELECT value FROM STRING_SPLIT(#Ids, ','))
Best and simple approach.
DECLARE #AccumulateKeywordCopy NVARCHAR(2000),#IDDupCopy NVARCHAR(50);
SET #AccumulateKeywordCopy ='';
SET #IDDupCopy ='';
SET #IDDup = (SELECT CONVERT(VARCHAR(MAX), <columnName>) FROM <tableName> WHERE <clause>)
SET #AccumulateKeywordCopy = ','+#AccumulateKeyword+',';
SET #IDDupCopy = ','+#IDDup +',';
SET #IDDupCheck = CHARINDEX(#IDDupCopy,#AccumulateKeywordCopy)
CREATE TABLE t
(
id INT,
col1 VARCHAR(50)
)
INSERT INTO t
VALUES (1,
'param1')
INSERT INTO t
VALUES (2,
'param2')
INSERT INTO t
VALUES (3,
'param3')
INSERT INTO t
VALUES (4,
'param4')
INSERT INTO t
VALUES (5,
'param5')
DECLARE #params VARCHAR(100)
SET #params = ',param1,param2,param3,'
SELECT *
FROM t
WHERE Charindex(',' + Cast(col1 AS VARCHAR(8000)) + ',', #params) > 0
working fiddle find here Fiddle
I ran into the same issue, and I don't want to have any footprint on the source database - i.e. no stored procedures or functions. I went about it this way:
declare #IDs table (Value int)
insert into #IDs values(1)
insert into #IDs values(2)
insert into #IDs values(3)
insert into #IDs values(5)
insert into #IDs values(4)
insert into #IDs values(6)
insert into #IDs values(7)
insert into #IDs values(98)
insert into #IDs values(234)
SELECT *
FROM sometable
WHERE tableid IN (select Value from #IDs)
The answer by #RBarryYoung (above) worked for me.
But when you have spaces in between the comma separated string values, then it will omit IDs with spaces. So I removed the spaces.
Take a look at the code snippet below.
Declare #Ids varchar(50) Set #Ids = '1 , 2,3'
set #Ids=','+Replace(#Ids,' ', '')+',';
Select * from [tblEmployee]
where Charindex(','+cast(ID as varchar(8000))+',', #Ids) > 0
WHERE someId IN (SELECT convert(int, value) FROM string_split(#stringOfCommaDelimitedIds, ','))
The simplest way i found was to use FIND_IN_SET
FIND_IN_SET(column_name, values)
values=(1,2,3)
SELECT name WHERE FIND_IN_SET(id, values)
This came in handy for one of my requirements where I did not want to use CTE and also did not want to go with the inner join.
DECLARE #Ids varchar(50);
SET #Ids = '1,2,3,5,4,6,7,98,234';
SELECT
cn1,cn2,cn3
FROM tableName
WHERE columnName in (select Value from fn_SplitString(#ids, ','))
Function for split string :
CREATE FUNCTION [dbo].[fn_SplitString] ( #stringToSplit VARCHAR(MAX), #seperator Char )
RETURNS
#returnList TABLE ([Value] [nvarchar] (500))
AS
BEGIN
DECLARE #name NVARCHAR(255)
DECLARE #pos INT
WHILE CHARINDEX(#seperator, #stringToSplit) > 0
BEGIN
SELECT #pos = CHARINDEX(#seperator, #stringToSplit)
SELECT #name = SUBSTRING(#stringToSplit, 1, #pos-1)
INSERT INTO #returnList
SELECT #name
SELECT #stringToSplit = SUBSTRING(#stringToSplit, #pos+1, LEN(#stringToSplit)-#pos)
END
INSERT INTO #returnList
SELECT #stringToSplit
RETURN
END

Comparing Comma Separate Value with Comma Separated Values SQL

I have a table A which contain column "ColumnName" (it will contain the comma separated integer values ) and I have store procedure which take parameter which is also comma separated integer values.
For example I have values in table "101,102,103" and "103,104,105" and User input is "101,104" it should return 2 record. how can I achieve this?
Need SQL statements
This should do it:
CREATE PROCEDURE GetMatches(#input varchar (100))
AS
BEGIN
WITH CTE AS
(
SELECT value AS number
FROM STRING_SPLIT(#input, ',')
)
SELECT CTE.number, A.ColumnName
FROM A
INNER JOIN CTE
ON ',' + A.ColumnName + ',' LIKE '%,' + CTE.number + ',%';
END
You can test the stored procedure like this:
EXEC dbo.GetMatches #input = '101,104';
If you need help, explanations, let me know :)
DECLARE
#input VARCHAR(MAX) = '101,104',
#separator CHAR(1) =',',
#separatorPosition INT,
#value VARCHAR(MAX),
#start INT = 1
DECLARE #split_input TABLE(Value VARCHAR(MAX))
DECLARE #tmp TABLE (st VARCHAR(50))
INSERT INTO #tmp VALUES ('101,102,103')
INSERT INTO #tmp VALUES ('103,104,105')
INSERT INTO #tmp VALUES ('106,107,108')
SET #separatorPosition = CHARINDEX(#separator, #input)
IF #separatorPosition = 0
BEGIN
INSERT INTO #split_input
VALUES
(
#input
)
RETURN
END
SET #input = #input + #separator
WHILE #separatorPosition > 0
BEGIN
SET #value = SUBSTRING(#input, #start, #separatorPosition - #start)
IF (#value <> '')
INSERT INTO #split_input
VALUES
(
#value
)
SET #start = #separatorPosition + 1
SET #separatorPosition = CHARINDEX(#separator, #input, #start)
END
SELECT tmp.st FROM #tmp AS tmp INNER JOIN #split_input AS split ON tmp.st LIKE '%' + split.Value + '%'
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION [dbo].[fnParseArray] (#Array VARCHAR(MAX),#separator CHAR(1))
RETURNS #T Table (col1 varchar(50))
AS
BEGIN
-- #Array is the array we wish to parse
-- #Separator is the separator charactor such as a comma
DECLARE #separator_position INT -- This is used to locate each separator character
DECLARE #array_value VARCHAR(MAX) -- this holds each array value as it is returned
-- For my loop to work I need an extra separator at the end. I always look to the
-- left of the separator character for each array value
SET #array = #array + #separator
-- Loop through the string searching for separtor characters
WHILE PATINDEX('%' + #separator + '%', #array) <> 0
BEGIN
-- patindex matches the a pattern against a string
SELECT #separator_position = PATINDEX('%' + #separator + '%',#array)
SELECT #array_value = LEFT(#array, #separator_position - 1)
-- This is where you process the values passed.
INSERT into #T VALUES (#array_value)
-- Replace this select statement with your processing
-- #array_value holds the value of this element of the array
-- This replaces what we just processed with and empty string
SELECT #array = STUFF(#array, 1, #separator_position, '')
END
RETURN
END
select * from [dbo].[fnParseArray]('a,b,c,d',',')

convert string into int SQL Server

This is the scenario:
My app will have the following:
A listbox (The checkbox property enabled) that will display a list of Something.
The user will select from the listbox (multiselect) by using the checkbox.
I will loop into All the checked items and store the ID's into an array. I will store the ID's into something like this separating the ID with a comma (1,2,3,4) and then I will use length -1 to delete the last comma.
How can I convert the string 1,2,3,4 into an integer type of data if my stored procedure is like this?
Select * from tblSomething Where ID in (1,2,3,4)
You can use the following SQL function.
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION [dbo].[CommaSeparatedToString]
(
#psCSString VARCHAR(8000)
)
RETURNS #otTemp TABLE(sID VARCHAR(20))
AS
BEGIN
DECLARE #sTemp VARCHAR(50)
WHILE LEN(#psCSString) > 0
BEGIN
SET #sTemp = LEFT(#psCSString, ISNULL(NULLIF(CHARINDEX(',', #psCSString) - 1, -1),
LEN(#psCSString)))
SET #psCSString = SUBSTRING(#psCSString,ISNULL(NULLIF(CHARINDEX(',', #psCSString), 0),
LEN(#psCSString)) + 1, LEN(#psCSString))
INSERT INTO #otTemp VALUES (#sTemp)
END
RETURN
END
And call in your stored procedure like
Select * from tblSomething
Where ID in (SELECT * FROM CommaSeparatedToString('1,2,3,4'))
You can use the
SELECT CAST(MyVarcharCol AS INT) FROM Table
SELECT CONVERT(INT, MyVarcharCol) FROM Table
refer this link
http://msdn.microsoft.com/en-us/library/ms187928.aspx
You need to create dynamic query for this
e.g you are getting list of values in #values paramter so prepare and run the dynamic query like this
DECLARE #query NVARCHAR(500)
DECLARE #values VARCHAR(200)
SET #values='1,2'
SET #query =N'Select * from tblSomething Where ID in ( ' + #values + ')'
SELECT #query
EXEC #Query
Use this function to split the value:
CREATE FUNCTION [dbo].[udfSplitCSV]
(
#String varchar (max),
#Delimiter varchar (10) = ','
)
RETURNS #ValueTable TABLE ([Row] int IDENTITY(1,1), [Value] varchar(max), [Length] int, [Duplicate] int NULL)
BEGIN
DECLARE #NextString varchar(max)
DECLARE #Pos int
DECLARE #NextPos int
IF #String IS NULL RETURN
--Initialize
SET #NextString = ''
SET #String = #String + #Delimiter
--Get position of first Comma
SET #Pos = charindex(#Delimiter,#String)
SET #NextPos = 1
--Loop while there is still a comma in the String
WHILE (#Pos <> 0)
BEGIN
SET #NextString = RTrim(LTrim(SubString(#String,1,#Pos - 1)))
INSERT INTO #ValueTable ([Value], [Length]) VALUES (#NextString, Len(#NextString))
SET #String = SubString(#String,#Pos+1,Len(#String))
SET #NextPos = #Pos
SET #Pos = CharIndex(#Delimiter,#String)
END
UPDATE #ValueTable
SET [Duplicate] = X.Duplicate
FROM #ValueTable VT
INNER JOIN (Select [Row], [Value], Row_Number() OVER (Partition By [Value] ORDER BY [Value], [Row]) as Duplicate FROM #ValueTable) X
ON X.[Row] = VT.[Row]
RETURN
END
-- Select * from dbo.udfSplitCSV('a , c b,c, a', ',')
When you are storing a bunch of IDs into the array, store with single quote.
so it will be ('1','2','3').
Then you no need to covert IDs into integer.

How to convert comma separated NVARCHAR to table records in SQL Server 2005?

I have a list of ids separated by comma like:
1,17,25,44,46,67,88
I want to convert them to a table records ( into a temporary table ) like
#tempTable
number_
--------
1
17
25
44
46
67
88
It is possible with a function, a table-valued one ?
Why I want this ? I want to use for INNER JOIN clause (into stored procedure) with another table(s) like as:
SELECT a,b,c FROM T1
INNER JOIN functionNameWhichReturnsTable
ON functionNameWhichReturnsTable.number_ = T1.a
I cannot use IN because I will use stored procedure which accepts a parameter of type NVARCHAR. That parameter will provide the list of ids.
Thank you
Possible duplicate of separate comma separated values and store in table in sql server.
Please try a precise one from Comma-Delimited Value to Table:
CREATE FUNCTION [dbo].[ufn_CSVToTable] ( #StringInput VARCHAR(8000), #Delimiter nvarchar(1))
RETURNS #OutputTable TABLE ( [String] VARCHAR(10) )
AS
BEGIN
DECLARE #String VARCHAR(10)
WHILE LEN(#StringInput) > 0
BEGIN
SET #String = LEFT(#StringInput,
ISNULL(NULLIF(CHARINDEX(#Delimiter, #StringInput) - 1, -1),
LEN(#StringInput)))
SET #StringInput = SUBSTRING(#StringInput,
ISNULL(NULLIF(CHARINDEX(#Delimiter, #StringInput), 0),
LEN(#StringInput)) + 1, LEN(#StringInput))
INSERT INTO #OutputTable ( [String] )
VALUES ( #String )
END
RETURN
END
GO
Check the requirement in other way using XML:
DECLARE #param NVARCHAR(MAX)
SET #param = '1:0,2:1,3:1,4:0'
SELECT
Split.a.value('.', 'VARCHAR(100)') AS CVS
FROM
(
SELECT CAST ('<M>' + REPLACE(#param, ',', '</M><M>') + '</M>' AS XML) AS CVS
) AS A CROSS APPLY CVS.nodes ('/M') AS Split(a)
Here's a trick that doesn't need a function or XML.
Basically the string gets transformed into a single insert statement for a temporary table.
The temp table can then be used for further processing.
IF OBJECT_ID('tempdb..#tmpNum') IS NOT NULL
DROP TABLE #tmpNum;
CREATE TABLE #tmpNum (num int);
DECLARE #TEXT varchar(max) = '1,17,25,44,46,67,88';
DECLARE #InsertStatement varchar(max);
SET #InsertStatement = 'insert into #tmpNum (num) values ('+REPLACE(#TEXT,',','),(')+');';
EXEC (#InsertStatement);
-- use the temp table
SELECT *
FROM YourTable t
WHERE t.id IN (SELECT DISTINCT num FROM #tmpNum);
This method is usable for up to 1000 values.
Because 1000 is the max limit of a row value expression.
Also, as Stuart Ainsworth pointed out.
Since this method uses Dynamic Sql, be wary of code injection and don't use it for strings based on user input.
Side-note
Starting from MS Sql Server 2016, one could simply use the STRING_SPLIT function.
DECLARE #TEXT varchar(max);
SET #TEXT = '1,17,25,44,46,67,88';
SELECT t.*
FROM YourTable t
JOIN (SELECT DISTINCT CAST(value AS INT) num FROM STRING_SPLIT(#TEXT, ',')) nums
ON t.id = nums.num;
Completing the answers, you could also use the CSV string to store multiple values in multiple columns:
--input sql text
declare #text_IN varchar(max) ='text1, text1.2, text1.3, 1, 2010-01-01\r\n text2, text2.2, text2.3, 2, 2016-01-01'
Split the csv file into rows:
declare #temptable table (csvRow varchar(max))
declare #DelimiterInit varchar(4) = '\r\n'
declare #Delimiter varchar(1) = '|'
declare #idx int
declare #slice varchar(max)
set #text_IN = REPLACE(#text_IN,#DelimiterInit,#Delimiter)
select #idx = 1
if len(#text_IN)<1 or #text_IN is null return
while #idx!= 0
begin
set #idx = charindex(#Delimiter,#text_IN)
if #idx!=0
set #slice = left(#text_IN,#idx - 1)
else
set #slice = #text_IN
if(len(#slice)>0)
insert into #temptable(csvRow) values(#slice)
set #text_IN = right(#text_IN,len(#text_IN) - #idx)
if len(#text_IN) = 0 break
end
Split rows into columns:
;WITH XMLTable (xmlTag)
AS
(
SELECT CONVERT(XML,'<CSV><champ>' + REPLACE(csvRow,',', '</champ><champ>') + '</champ></CSV>') AS xmlTag
FROM #temptable
)
SELECT RTRIM(LTRIM(xmlTag.value('/CSV[1]/champ[1]','varchar(max)'))) AS Column1,
RTRIM(LTRIM(xmlTag.value('/CSV[1]/champ[2]','varchar(max)'))) AS Column2,
RTRIM(LTRIM(xmlTag.value('/CSV[1]/champ[3]','varchar(max)'))) AS Column3,
RTRIM(LTRIM(xmlTag.value('/CSV[1]/champ[4]','int'))) AS Column4,
RTRIM(LTRIM(xmlTag.value('/CSV[1]/champ[5]','datetime'))) AS Column5
FROM XMLTable
The following works:
declare #parStoreNo As varchar(8000) = '1,2,3,4'
CREATE TABLE #parStoreNo (StoreNo INT)-- drop #parStoreNo
declare #temptable VARCHAR(1000) = #parStoreNo
declare #SQL VARCHAR(1000)
SELECT #SQL = CONVERT(VARCHAR(1000),' select ' + REPLACE(ISNULL(#temptable,' NULL '),',', ' AS Col UNION ALL SELECT '))
INSERT #parStoreNo (StoreNo)
EXEC (#SQL)
I am using XML Function as below...
DECLARE #str VARCHAR(4000) = '6,7,7,8,10,12,13,14,16,44,46,47,394,396,417,488,714,717,718,719,722,725,811,818,832,833,836,837,846,913,914,919,922,923,924,925,926,927,927,928,929,929,930,931,932,934,935,1029,1072,1187,1188,1192,1196,1197,1199,1199,1199,1199,1200,1201,1202,1203,1204,1205,1206,1207,1208,1209,1366,1367,1387,1388,1666,1759,1870,2042,2045,2163,2261,2374,2445,2550,2676,2879,2880,2881,2892,2893,2894'
Declare #x XML
select #x = cast('<A>'+ replace(#str,',','</A><A>')+ '</A>' as xml)
select t.value('.', 'int') as inVal
from #x.nodes('/A') as x(t)
I prefer this because not need to create any separate function and proc. Also I don't have to opt dynamic SQL query which I prefer most.
Convert Comma Separated String to Table
DECLARE #str VARCHAR(4000) = '6,7,7,8,10,12,13,14,16,44,46,47,394,396,417,488,714,717,718,719,722,725,811,818,832'
DECLARE #x XML
select #x = cast('<A>'+ replace(#str,',','</A><A>')+ '</A>' as xml)
select t.value('.', 'int') as inVal
from #x.nodes('/A') as x(t)
Try this code
SELECT RTRIM(part) as part
INTO Table_Name
FROM dbo.splitstring(#Your_Comma_string,',')
splitstring Function is as follows
CREATE FUNCTION dbo.splitstring ( #stringToSplit VARCHAR(MAX) )
RETURNS
#returnList TABLE ([Name] [nvarchar] (500))
AS
BEGIN
DECLARE #name NVARCHAR(255)
DECLARE #pos INT
WHILE CHARINDEX(',', #stringToSplit) > 0
BEGIN
SELECT #pos = CHARINDEX(',', #stringToSplit)
SELECT #name = SUBSTRING(#stringToSplit, 1, #pos-1)
INSERT INTO #returnList
SELECT #name
SELECT #stringToSplit = SUBSTRING(#stringToSplit, #pos+1, LEN(#stringToSplit)-#pos)
END
INSERT INTO #returnList
SELECT #stringToSplit
RETURN
END

Passing a varchar full of comma delimited values to a SQL Server IN function

Duplicate of
Dynamic SQL Comma Delimited Value Query
Parameterized Queries with Like and In
I have a SQL Server Stored Procedure where I would like to pass a varchar full of comma delimited values to an IN function. For example:
DECLARE #Ids varchar(50);
SET #Ids = '1,2,3,5,4,6,7,98,234';
SELECT *
FROM sometable
WHERE tableid IN (#Ids);
This does not work of course. I get the error:
Conversion failed when converting the varchar value '1,2,3,5,4,6,7,98,234' to data type int.
How can I accomplish this (or something relatively similar) without resorting to building dynamic SQL?
Of course if you're lazy like me, you could just do this:
Declare #Ids varchar(50) Set #Ids = ',1,2,3,5,4,6,7,98,234,'
Select * from sometable
where Charindex(','+cast(tableid as varchar(8000))+',', #Ids) > 0
Don't use a function that loops to split a string!, my function below will split a string very fast, with no looping!
Before you use my function, you need to set up a "helper" table, you only need to do this one time per database:
CREATE TABLE Numbers
(Number int NOT NULL,
CONSTRAINT PK_Numbers PRIMARY KEY CLUSTERED (Number ASC)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
DECLARE #x int
SET #x=0
WHILE #x<8000
BEGIN
SET #x=#x+1
INSERT INTO Numbers VALUES (#x)
END
use this function to split your string, which does not loop and is very fast:
CREATE FUNCTION [dbo].[FN_ListToTable]
(
#SplitOn char(1) --REQUIRED, the character to split the #List string on
,#List varchar(8000) --REQUIRED, the list to split apart
)
RETURNS
#ParsedList table
(
ListValue varchar(500)
)
AS
BEGIN
/**
Takes the given #List string and splits it apart based on the given #SplitOn character.
A table is returned, one row per split item, with a column name "ListValue".
This function workes for fixed or variable lenght items.
Empty and null items will not be included in the results set.
Returns a table, one row per item in the list, with a column name "ListValue"
EXAMPLE:
----------
SELECT * FROM dbo.FN_ListToTable(',','1,12,123,1234,54321,6,A,*,|||,,,,B')
returns:
ListValue
-----------
1
12
123
1234
54321
6
A
*
|||
B
(10 row(s) affected)
**/
----------------
--SINGLE QUERY-- --this will not return empty rows
----------------
INSERT INTO #ParsedList
(ListValue)
SELECT
ListValue
FROM (SELECT
LTRIM(RTRIM(SUBSTRING(List2, number+1, CHARINDEX(#SplitOn, List2, number+1)-number - 1))) AS ListValue
FROM (
SELECT #SplitOn + #List + #SplitOn AS List2
) AS dt
INNER JOIN Numbers n ON n.Number < LEN(dt.List2)
WHERE SUBSTRING(List2, number, 1) = #SplitOn
) dt2
WHERE ListValue IS NOT NULL AND ListValue!=''
RETURN
END --Function FN_ListToTable
you can use this function as a table in a join:
SELECT
Col1, COl2, Col3...
FROM YourTable
INNER JOIN FN_ListToTable(',',#YourString) s ON YourTable.ID = s.ListValue
Here is your example:
Select * from sometable where tableid in(SELECT ListValue FROM dbo.FN_ListToTable(',',#Ids) s)
No Table No Function No Loop
Building on the idea of parsing your list into a table our DBA suggested using XML.
Declare #Ids varchar(50)
Set #Ids = ‘1,2,3,5,4,6,7,98,234’
DECLARE #XML XML
SET #XML = CAST('<i>' + REPLACE(#Ids, ',', '</i><i>') + '</i>' AS XML)
SELECT *
FROM
SomeTable
INNER JOIN #XML.nodes('i') x(i)
ON SomeTable .Id = x.i.value('.', 'VARCHAR(MAX)')
These seems to have the same performance as #KM's answer but, I think, a lot simpler.
You can create a function that returns a table.
so your statement would be something like
select * from someable
join Splitfunction(#ids) as splits on sometable.id = splits.id
Here is a simular function.
CREATE FUNCTION [dbo].[FUNC_SplitOrderIDs]
(
#OrderList varchar(500)
)
RETURNS
#ParsedList table
(
OrderID int
)
AS
BEGIN
DECLARE #OrderID varchar(10), #Pos int
SET #OrderList = LTRIM(RTRIM(#OrderList))+ ','
SET #Pos = CHARINDEX(',', #OrderList, 1)
IF REPLACE(#OrderList, ',', '') <> ''
BEGIN
WHILE #Pos > 0
BEGIN
SET #OrderID = LTRIM(RTRIM(LEFT(#OrderList, #Pos - 1)))
IF #OrderID <> ''
BEGIN
INSERT INTO #ParsedList (OrderID)
VALUES (CAST(#OrderID AS int)) --Use Appropriate conversion
END
SET #OrderList = RIGHT(#OrderList, LEN(#OrderList) - #Pos)
SET #Pos = CHARINDEX(',', #OrderList, 1)
END
END
RETURN
END
It's a very common question. Canned answer, several nice techniques:
http://www.sommarskog.se/arrays-in-sql-2005.html
This works perfectly! The below answers are too complicated. Don't look at this as dynamic. Set up your store procedure as follows:
(#id as varchar(50))
as
Declare #query as nvarchar(max)
set #query ='
select * from table
where id in('+#id+')'
EXECUTE sp_executesql #query
I can suggest using WITH like this:
DECLARE #Delim char(1) = ',';
SET #Ids = #Ids + #Delim;
WITH CTE(i, ls, id) AS (
SELECT 1, CHARINDEX(#Delim, #Ids, 1), SUBSTRING(#Ids, 1, CHARINDEX(#Delim, #Ids, 1) - 1)
UNION ALL
SELECT i + 1, CHARINDEX(#Delim, #Ids, ls + 1), SUBSTRING(#Ids, ls + 1, CHARINDEX(#Delim, #Ids, ls + 1) - CHARINDEX(#Delim, #Ids, ls) - 1)
FROM CTE
WHERE CHARINDEX(#Delim, #Ids, ls + 1) > 1
)
SELECT t.*
FROM yourTable t
INNER JOIN
CTE c
ON t.id = c.id;
I think a very simple solution could be following:
DECLARE #Ids varchar(50);
SET #Ids = '1,2,3,5,4,6,7,98,234';
SELECT *
FROM sometable
WHERE ','+#Ids+',' LIKE '%,'+CONVERT(VARCHAR(50),tableid)+',%';
Without using dynamic SQL, you have to take the input variable and use a split function to put the data into a temp table and then join to that.
Thanks, for your function I Used IT........................
This is my EXAMPLE
**UPDATE [RD].[PurchaseOrderHeader]
SET [DispatchCycleNumber] ='10'
WHERE OrderNumber in(select * FROM XA.fn_SplitOrderIDs(#InvoiceNumberList))**
CREATE FUNCTION [XA].[fn_SplitOrderIDs]
(
#OrderList varchar(500)
)
RETURNS
#ParsedList table
(
OrderID int
)
AS
BEGIN
DECLARE #OrderID varchar(10), #Pos int
SET #OrderList = LTRIM(RTRIM(#OrderList))+ ','
SET #Pos = CHARINDEX(',', #OrderList, 1)
IF REPLACE(#OrderList, ',', '') <> ''
BEGIN
WHILE #Pos > 0
BEGIN
SET #OrderID = LTRIM(RTRIM(LEFT(#OrderList, #Pos - 1)))
IF #OrderID <> ''
BEGIN
INSERT INTO #ParsedList (OrderID)
VALUES (CAST(#OrderID AS int)) --Use Appropriate conversion
END
SET #OrderList = RIGHT(#OrderList, LEN(#OrderList) - #Pos)
SET #Pos = CHARINDEX(',', #OrderList, 1)
END
END
RETURN
END
If you use SQL Server 2008 or higher, use table valued parameters; for example:
CREATE PROCEDURE [dbo].[GetAccounts](#accountIds nvarchar)
AS
BEGIN
SELECT *
FROM accountsTable
WHERE accountId IN (select * from #accountIds)
END
CREATE TYPE intListTableType AS TABLE (n int NOT NULL)
DECLARE #tvp intListTableType
-- inserts each id to one row in the tvp table
INSERT #tvp(n) VALUES (16509),(16685),(46173),(42925),(46167),(5511)
EXEC GetAccounts #tvp
Its been a while but I have done this in the past using XML as a interim.
I can't take any credit for this, but I'm afraid I no longer know where I got this idea from:
-- declare the variables needed
DECLARE #xml as xml,#str as varchar(100),#delimiter as varchar(10)
-- The string you want to split
SET #str='A,B,C,D,E,Bert,Ernie,1,2,3,4,5'
-- What you want to split on. Can be a single character or a string
SET #delimiter =','
-- Convert it to an XML document
SET #xml = cast(('<X>'+replace(#str,#delimiter ,'</X><X>')+'</X>') as xml)
-- Select back from the XML
SELECT N.value('.', 'varchar(10)') as value FROM #xml.nodes('X') as T(N)
Create a table function like below which parse comma separated varchar and returns a table that can be inner joined with other tables.
CREATE FUNCTION [dbo].[fn_SplitList]
(
#inString varchar(MAX) = '',
#inDelimiter char(1) = ',' -- Keep the delimiter to 100 chars or less. Generally a delimiter will be 1-2 chars only.
)
RETURNS #tbl_Return table
(
Unit varchar(1000) COLLATE Latin1_General_BIN
)
AS
BEGIN
INSERT INTO #tbl_Return
SELECT DISTINCT
LTRIM(RTRIM(piece.value('./text()[1]', 'varchar(1000)'))) COLLATE DATABASE_DEFAULT AS Unit
FROM
(
--
-- Replace any delimiters in the string with the "X" tag.
--
SELECT
CAST(('<X>' + REPLACE(s0.prsString, s0.prsSplitDelimit, '</X><X>') + '</X>') AS xml).query('.') AS units
FROM
(
--
-- Convert the string and delimiter into XML.
--
SELECT
(SELECT #inString FOR XML PATH('')) AS prsString,
(SELECT #inDelimiter FOR XML PATH('')) AS prsSplitDelimit
) AS s0
) AS s1
CROSS APPLY units.nodes('X') x(piece)
RETURN
END
=================================================
Now consume above created table function in your code,creation of function is one time activity in your database that can be used across databases as well on same server.
DECLARE #Ids varchar(50);
SET #Ids = '1,2,3,5,4,6,7,98,234';
SELECT
*
FROM sometable AS st
INNER JOIN fn_SplitList(#ids, ',') AS sl
ON sl.unit = st.tableid
I've written a stored procedure to show how to do this before.
You basically have to process the string.
I tried to post the code here but the formatting got all screwy.
IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = object_id(N'[dbo].[uspSplitTextList]') AND OBJECTPROPERTY(id, N'IsProcedure') = 1)
DROP PROCEDURE [dbo].[uspSplitTextList]
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_NULLS ON
GO
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
-- uspSplitTextList
--
-- Description:
-- splits a separated list of text items and returns the text items
--
-- Arguments:
-- #list_text - list of text items
-- #Delimiter - delimiter
--
-- Notes:
-- 02/22/2006 - WSR : use DATALENGTH instead of LEN throughout because LEN doesn't count trailing blanks
--
-- History:
-- 02/22/2006 - WSR : revised algorithm to account for items crossing 8000 character boundary
-- 09/18/2006 - WSR : added to this project
--
CREATE PROCEDURE uspSplitTextList
#list_text text,
#Delimiter varchar(3)
AS
SET NOCOUNT ON
DECLARE #InputLen integer -- input text length
DECLARE #TextPos integer -- current position within input text
DECLARE #Chunk varchar(8000) -- chunk within input text
DECLARE #ChunkPos integer -- current position within chunk
DECLARE #DelimPos integer -- position of delimiter
DECLARE #ChunkLen integer -- chunk length
DECLARE #DelimLen integer -- delimiter length
DECLARE #ItemBegPos integer -- item starting position in text
DECLARE #ItemOrder integer -- item order in list
DECLARE #DelimChar varchar(1) -- first character of delimiter (simple delimiter)
-- create table to hold list items
-- actually their positions because we may want to scrub this list eliminating bad entries before substring is applied
CREATE TABLE #list_items ( item_order integer, item_begpos integer, item_endpos integer )
-- process list
IF #list_text IS NOT NULL
BEGIN
-- initialize
SET #InputLen = DATALENGTH(#list_text)
SET #TextPos = 1
SET #DelimChar = SUBSTRING(#Delimiter, 1, 1)
SET #DelimLen = DATALENGTH(#Delimiter)
SET #ItemBegPos = 1
SET #ItemOrder = 1
SET #ChunkLen = 1
-- cycle through input processing chunks
WHILE #TextPos <= #InputLen AND #ChunkLen <> 0
BEGIN
-- get current chunk
SET #Chunk = SUBSTRING(#list_text, #TextPos, 8000)
-- setup initial variable values
SET #ChunkPos = 1
SET #ChunkLen = DATALENGTH(#Chunk)
SET #DelimPos = CHARINDEX(#DelimChar, #Chunk, #ChunkPos)
-- loop over the chunk, until the last delimiter
WHILE #ChunkPos <= #ChunkLen AND #DelimPos <> 0
BEGIN
-- see if this is a full delimiter
IF SUBSTRING(#list_text, (#TextPos + #DelimPos - 1), #DelimLen) = #Delimiter
BEGIN
-- insert position
INSERT INTO #list_items (item_order, item_begpos, item_endpos)
VALUES (#ItemOrder, #ItemBegPos, (#TextPos + #DelimPos - 1) - 1)
-- adjust positions
SET #ItemOrder = #ItemOrder + 1
SET #ItemBegPos = (#TextPos + #DelimPos - 1) + #DelimLen
SET #ChunkPos = #DelimPos + #DelimLen
END
ELSE
BEGIN
-- adjust positions
SET #ChunkPos = #DelimPos + 1
END
-- find next delimiter
SET #DelimPos = CHARINDEX(#DelimChar, #Chunk, #ChunkPos)
END
-- adjust positions
SET #TextPos = #TextPos + #ChunkLen
END
-- handle last item
IF #ItemBegPos <= #InputLen
BEGIN
-- insert position
INSERT INTO #list_items (item_order, item_begpos, item_endpos)
VALUES (#ItemOrder, #ItemBegPos, #InputLen)
END
-- delete the bad items
DELETE FROM #list_items
WHERE item_endpos < item_begpos
-- return list items
SELECT SUBSTRING(#list_text, item_begpos, (item_endpos - item_begpos + 1)) AS item_text, item_order, item_begpos, item_endpos
FROM #list_items
ORDER BY item_order
END
DROP TABLE #list_items
RETURN
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
GO
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO
I have same idea with user KM. but do not need extra table Number. Just this function only.
CREATE FUNCTION [dbo].[FN_ListToTable]
(
#SplitOn char(1) --REQUIRED, the character to split the #List string on
,#List varchar(8000) --REQUIRED, the list to split apart
)
RETURNS
#ParsedList table
(
ListValue varchar(500)
)
AS
BEGIN
DECLARE #number int = 0
DECLARE #childString varchar(502) = ''
DECLARE #lengthChildString int = 0
DECLARE #processString varchar(502) = #SplitOn + #List + #SplitOn
WHILE #number < LEN(#processString)
BEGIN
SET #number = #number + 1
SET #lengthChildString = CHARINDEX(#SplitOn, #processString, #number + 1) - #number - 1
IF #lengthChildString > 0
BEGIN
SET #childString = LTRIM(RTRIM(SUBSTRING(#processString, #number + 1, #lengthChildString)))
IF #childString IS NOT NULL AND #childString != ''
BEGIN
INSERT INTO #ParsedList(ListValue) VALUES (#childString)
SET #number = #number + #lengthChildString - 1
END
END
END
RETURN
END
And here is the test:
SELECT ListValue FROM dbo.FN_ListToTable('/','a/////bb/c')
Result:
ListValue
______________________
a
bb
c
-- select * from dbo.Split_ID('77,106')
ALTER FUNCTION dbo.Split_ID(#String varchar(8000))
returns #temptable TABLE (ID varchar(8000))
as
begin
declare #idx int
declare #slice varchar(8000)
declare #Delimiter char(1)
set #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(ID) values(#slice)
set #String = right(#String,len(#String) - #idx)
if len(#String) = 0 break
end
return
end
You could do it like this:
create or replace
PROCEDURE UDP_SETBOOKMARK
(
P_USERID IN VARCHAR2
, P_BOOKMARK IN VARCHAR2
) AS
BEGIN
UPDATE T_ER_Bewertung
SET LESEZEICHEN = P_BOOKMARK
WHERE STAMM_ID in( select regexp_substr(P_USERID,'[^,]+', 1, level) from dual
connect by regexp_substr(P_USERID, '[^,]+', 1, level) is not null )
and ER_ID = (select max(ER_ID) from T_ER_Bewertung_Kopie);
commit;
END UDP_SETBOOKMARK;
Then try it with
Begin
UDP_SETBOOKMARK ('1,2,3,4,5', 'Test');
End;
You can use this IN-Clause with regexp_substr in other situations too, just try it.
Error 493: The column 'i' that was returned from the nodes() method cannot be
used directly. It can only be used with one of the four XML data type
methods, exist(), nodes(), query(), and value(), or in IS NULL and IS NOT
NULL checks.
The above errorr was fixed in SQL Server 2014 by using following snippet
Declare #Ids varchar(50)
Set #Ids = '1,2,3,5,4,6,7,98,234'
DECLARE #XML XML
SET #XML = CAST('<i>' + REPLACE(#Ids, ',', '</i><i>') + '</i>' AS XML)
SELECT SomeTable.*
FROM
SomeTable
cross apply #XML.nodes('i') x(i)
where SomeTable .Id = x.i.value('.', 'VARCHAR(MAX)')
Try this:
SELECT ProductId, Name, Tags
FROM Product
WHERE '1,2,3,' LIKE '%' + CAST(ProductId AS VARCHAR(20)) + ',%';
As said on the last example of this link
Tons of answers here, but to add my two cents I think STRING_SPLIT is a very simple approach to this sort of problem:
DECLARE #Ids varchar(50);
SET #Ids = '1,2,3,5,4,6,7,98,234';
SELECT *
FROM sometable
WHERE tableid IN;
(SELECT value FROM STRING_SPLIT(#Ids, ','))
Best and simple approach.
DECLARE #AccumulateKeywordCopy NVARCHAR(2000),#IDDupCopy NVARCHAR(50);
SET #AccumulateKeywordCopy ='';
SET #IDDupCopy ='';
SET #IDDup = (SELECT CONVERT(VARCHAR(MAX), <columnName>) FROM <tableName> WHERE <clause>)
SET #AccumulateKeywordCopy = ','+#AccumulateKeyword+',';
SET #IDDupCopy = ','+#IDDup +',';
SET #IDDupCheck = CHARINDEX(#IDDupCopy,#AccumulateKeywordCopy)
CREATE TABLE t
(
id INT,
col1 VARCHAR(50)
)
INSERT INTO t
VALUES (1,
'param1')
INSERT INTO t
VALUES (2,
'param2')
INSERT INTO t
VALUES (3,
'param3')
INSERT INTO t
VALUES (4,
'param4')
INSERT INTO t
VALUES (5,
'param5')
DECLARE #params VARCHAR(100)
SET #params = ',param1,param2,param3,'
SELECT *
FROM t
WHERE Charindex(',' + Cast(col1 AS VARCHAR(8000)) + ',', #params) > 0
working fiddle find here Fiddle
I ran into the same issue, and I don't want to have any footprint on the source database - i.e. no stored procedures or functions. I went about it this way:
declare #IDs table (Value int)
insert into #IDs values(1)
insert into #IDs values(2)
insert into #IDs values(3)
insert into #IDs values(5)
insert into #IDs values(4)
insert into #IDs values(6)
insert into #IDs values(7)
insert into #IDs values(98)
insert into #IDs values(234)
SELECT *
FROM sometable
WHERE tableid IN (select Value from #IDs)
The answer by #RBarryYoung (above) worked for me.
But when you have spaces in between the comma separated string values, then it will omit IDs with spaces. So I removed the spaces.
Take a look at the code snippet below.
Declare #Ids varchar(50) Set #Ids = '1 , 2,3'
set #Ids=','+Replace(#Ids,' ', '')+',';
Select * from [tblEmployee]
where Charindex(','+cast(ID as varchar(8000))+',', #Ids) > 0
WHERE someId IN (SELECT convert(int, value) FROM string_split(#stringOfCommaDelimitedIds, ','))
The simplest way i found was to use FIND_IN_SET
FIND_IN_SET(column_name, values)
values=(1,2,3)
SELECT name WHERE FIND_IN_SET(id, values)
This came in handy for one of my requirements where I did not want to use CTE and also did not want to go with the inner join.
DECLARE #Ids varchar(50);
SET #Ids = '1,2,3,5,4,6,7,98,234';
SELECT
cn1,cn2,cn3
FROM tableName
WHERE columnName in (select Value from fn_SplitString(#ids, ','))
Function for split string :
CREATE FUNCTION [dbo].[fn_SplitString] ( #stringToSplit VARCHAR(MAX), #seperator Char )
RETURNS
#returnList TABLE ([Value] [nvarchar] (500))
AS
BEGIN
DECLARE #name NVARCHAR(255)
DECLARE #pos INT
WHILE CHARINDEX(#seperator, #stringToSplit) > 0
BEGIN
SELECT #pos = CHARINDEX(#seperator, #stringToSplit)
SELECT #name = SUBSTRING(#stringToSplit, 1, #pos-1)
INSERT INTO #returnList
SELECT #name
SELECT #stringToSplit = SUBSTRING(#stringToSplit, #pos+1, LEN(#stringToSplit)-#pos)
END
INSERT INTO #returnList
SELECT #stringToSplit
RETURN
END