How to separate string by comma using SQL Server? - sql

I wants to separate string by comma. Actually I wants to filter in WHERE Clause like
CREATE PROC spGetRecords
#class varchar(50)
AS
BEGIN
SELECT *
FROM SampleTable
WHERE class in (#class) --in Database class is an integer
END
So, I want to pass the parameter when execute query like below
spGetRecords #class = '12,22,45,66'
I think it's not possible to pass multiple value in single parameter in my case.
So, If I remove separate the string by ',' then I can run my query in while loop that will bring the record correct?
So, How can I separate the string by comma

You can try splitting using xml path as below:
Declare #delimiter nvarchar(max) = ','
Declare #str nvarchar(max) = '12,22,45,66'
Declare #xml as xml
Select #xml = CAST('<x>' + REPLACE((SELECT REPLACE(#str,#delimiter,'$$$SSText$$$') AS [*] FOR XML PATH('')),'$$$SSText$$$','</x><x>')+ '</x>' AS XML)
--select #xml
Select y.value(N'text()[1]', N'nvarchar(MAX)') as value
FROM #xml.nodes(N'x') as x(y)
If you are in SQL Server >= 2016 you can use STRING_SPLIT() as below:
Select * from string_split('12,22,45,66',',')

Use dynamic SQL
exec 'SELECT * FROM SampleTable WHERE class in (' + #class + ')'
which will patch the strings together and then execute it like this:
SELECT * FROM SampleTable WHERE class in (12,22,45,66)

Here is a good solution - Converting String List into Int List in SQL . Idea here is to send in an int list to a stored procedure as an XML parameter.
Here is another quick but dirty solution - t-sql Convert comma separated string into int, without using user created function . Here the passed in value is used within a dynamic sql statement.
More ideas - http://sql-articles.com/scripts/function-to-split-comma-separated-string-to-integer/

Related

IN keyword in Dynamic SQL

I have a stored procedure which is implemented in Dynamic SQL. I am trying to include an IN keyword and pass fixed values in the list between single quotes.
I want to write something like this:
where grp.Status in ('A,S')
but the above statement is already encapsulated in single quotes.
I tried using something like the one below:
where grp.Status in (' +QUOTENAME('A,S','''')+
but the query is only recognizing the first value in the list i.e. 'A'.
I also read about using a split function and putting the values in a temp table and using the temp table's column instead. But, I don't was to do that process for a tiny list.
Any suggestions and solutions are greatly appreciated.
You can just put the list into the SQL:
'where grp.Status in (''' + replace('A,S', ',', ''',''') + ''') . . .
If I got those single quotes right, this should produce the result as:
where grp.Status in ('A','S') . . .
If you do not want to use the split function you can do the following with your dynamic sql.
declare #xml xml
, #list Varchar(100) = 'A,B'
set #xml = N'<root><r>' + replace(#list, ',' ,'</r><r>') + '</r></root>'
Declare #Sql Nvarchar(MAX);
SET #Sql = N'Select * from TableName grp
WHERE grp.Status IN (
select r.value(''.'',''varchar(max)'') as item
from #xml.nodes(''//root/r'') as records(r)
)'
Exec sp_executesql #Sql
,N'#xml XML'
,#xml

How to perform a WHERE similar to "NOT IN" with a list of value coming from a HTTP FORM?

I have web application developed in C#, where a page is showing the output of a SQL query; the query is a linq call to a store procedure and I am using SQL Server 2008.
One of the column display the tags associated with the result line; on the same page a list of check-boxes is displayed, each check-box correspond to a tag, and if the user turn on or off one or multiple check-box I want to filter the query.
My simplest SQL solution would be to us "NOT IN" like the following:
select * from [mytable] where [tags] not in ('tag1','tag2, etc...)
Given I convert the FORM POST to a string with comma separated values.
Example:
string tags = ParseFormAndConvertCheckBoxToCSV(Page.Request.Form);
string sqlcmd = "select * from [mytable] where [tags] not in (" + tags + ")";
But I don't want to dynamically build the SQL because as far as I know that would be bad.
I can imagine few way of splitting the string with comma separated list of values in SQL nd store the values into a in-memory table:
declare #tagscsv nvarchar(MAX)
declare #notags TABLE(Value nvarchar(50))
set #tagscsv = 'taxi,ivf'
Declare #x XML
select #x = cast('<A>'+ replace(#tagscsv,',','</A><A>')+ '</A>' as xml)
insert into #notags
select t.value('.', 'nvarchar(50)') as v from #x.nodes('/A') as x(t)
select * from [mytable] where [tags] not in (select * from #notags)
...but this too sounds like a dirty trick.
To me it looks like this should be a very common situation, and I figure out a lot of people out there faced this problem in the past, but searching on google I could not find an elegant solution.
Anyone can help?
Edit: Missed the stored procedure part.
With stored procedures you do not have a lot of options. I usually do what you did, split the comma seperated string and save the values to a temp table (or table variable).
CREATE PROCEDURE SplitAndStuff
#List nvarchar(MAX) = NULL,
#SplitOn nvarchar(5) = ',' --This can be replaced with a literal if it is always a comma.
AS
BEGIN
DECLARE #Pos int;
DECLARE #SplitOnLength int;
DECLARE #Results TABLE (value nvarchar(MAX))
SET #SplitOnLength = DATALENGTH(#SplitOn) / 2;
IF (RIGHT(#List, #SplitOnLength) <> #SplitOn) SET #List = #List + #SplitOn; --Add trailling split string if there is not one already.
SET #Pos = CHARINDEX(#SplitOn, #List, 1) --Initalize for loop. (The starting position returned is 1-based, not 0-based.)
WHILE #Pos > 0
BEGIN
INSERT INTO #Results (value) SELECT CAST(SUBSTRING(#List,1,#Pos-1)AS int);
SET #List = SUBSTRING(#List, #Pos+#SplitOnLength, DATALENGTH(#List) / 2);
SET #Pos = CHARINDEX(#SplitOn, #List, 1);
END
END
GO
If your stored procedure is not doing anything else (besides the look up) you could use my original LINQ answer below:
Since you are using LINQ you need to split tagscsv and use the resulting array to perform an "where in" query:
string[] tags = tagscsv.Split(',');
var output = from q in db.Table where !tags.Contains(q) select q;
See also:
http://msdn.microsoft.com/en-us/library/ms132407.aspx
Using LINQ to Perform "WHERE IN (Value1,Value2)" Queries
The NOT IN clause in LINQ to SQL

TSQL Statement IN

I am having a small problem with the IN SQL statement. I was just wondering if anyone could help me?
#Ids = "1,2,3,4,5"
SELECT * FROM Nav WHERE CONVERT(VARCHAR,NavigationID) IN (CONVERT(VARCHAR,#Ids))
This is coming back with the error below, I am sure this is pretty simple!
Conversion failed when converting the varchar value '1,' to data type int.
The SQL IN clause does not accept a single variable to represent a list of values -- no database does, without using dynamic SQL. Otherwise, you could use a Table Valued Function (SQL Server 2000+) to pull the values out of the list & return them as a table that you can join against.
Dynamic SQL example:
EXEC('SELECT *
FROM Nav
WHERE NavigationID IN ('+ #Ids +')')
I recommend reading The curse and blessings of dynamic SQL before using dynamic SQL on SQL Server.
Jason:
First create a function like this
Create FUNCTION [dbo].[ftDelimitedAsTable](#dlm char, #string varchar(8000))
RETURNS
--------------------------------------------------------------------------*/
/*------------------------------------------------------------------------
declare #dlm char, #string varchar(1000)
set #dlm=','; set #string='t1,t2,t3';
-- tHIS FUNCION RETUNRS IN THE ASCENDING ORDER
-- 19TH Apr 06
------------------------------------------------------------------------*/
--declare
#table_var TABLE
(id int identity(1,1),
r varchar(1000)
)
AS
BEGIN
declare #n int,#i int
set #n=dbo.fnCountChars(#dlm,#string)+1
SET #I =1
while #I <= #N
begin
insert #table_var
select dbo.fsDelimitedString(#dlm,#string,#i)
set #I= #I+1
end
if #n =1 insert #TABLE_VAR VALUES(#STRING)
delete from #table_var where r=''
return
END
And then
set quoted_identifier off
declare #ids varchar(max)
select #Ids = "1,2,3,4,5"
declare #nav table ( navigationid int identity(1,1),theother bigint)
insert #nav(theother) select 10 union select 11 union select 15
SELECT * FROM #Nav WHERE CONVERT(VARCHAR,NavigationID) IN (select id from dbo.ftDelimitedAsTable(',',#Ids))
select * from dbo.ftDelimitedAsTable(',',#Ids)
What you're doing is not possible with the SQL IN statement. You cannot pass a string to it and expect that string to be parsed. IN is for specific, hard-coded values.
There are two ways to do what you want to do here.
One is to create a 'dynamic sql' query and execute it, after substituting in your IN list.
DECLARE #query varchar(max);
SET #query = 'SELECT * FROM Nav WHERE CONVERT(VARCHAR,NavigationID) IN (' + #Ids + ')'
exec (#query)
This can have performance impacts and other complications. Generally I'd try to avoid it.
The other method is to use a User Defined Function (UDF) to split the string into its component parts and then query against that.
There's a post detailing how to create that function here
Once the function exists, it's trivial to join onto it
SELECT * FROM Nav
CROSS APPLY dbo.StringSplit(#Ids) a
WHERE a.s = CONVERT(varchar, Nav.NavigationId)
NB- the 'a.s' field reference is based on the linked function, which stores the split value in a column named 's'. This may differ based on the implementation of your string split function
This is nice because it uses a set based approach to the query rather than an IN subquery, but a CROSS JOIN may be a little complex for the moment, so if you want to maintain the IN syntax then the following should work:
SELECT * FROM Nav
WHERE Nav.NavigationId IN
(SELECT CONVERT(int, a.s) AS Value
FROM dbo.StringSplit(#Ids) a

Dynamic SQL Comma-Delimited Value Query

[Update: Using SQL Server 2005]
Hi, what I want to do is query my stored procedure with a comma-delimited list of values (ids) to retrieve rows of data.
The problem I am receiving is a conversion error:
Conversion failed when converting the varchar value ' +
#PassedInIDs + ' to data type int.
The statement in my where-clause and error is:
...
AND (database.ID IN (' + #PassedInIDs + '))
Note: database.ID is of int type.
I was following the article at:
http://www.sql-server-helper.com/functions/comma-delimited-to-table.aspx
but did not complete because of the error.
In my execution script I have:
...
#PassedInIDs= '1,5'
Am I doing something wrong here?
Thank you for your help.
I would strongly suggest that you use the second method from that link. Create a user-defined function that turns your comma-delimited string into a table, which you can then select from easily.
If you do a Google on Erland and "Dynamic SQL" he has a good writeup of the pitfalls that it entails.
For one, you are passing a string to the IN function in SQL. If you look back at the original article, you'll see that instead of issuing a direct SQL statement, it instead is building a string which is the SQL statement to execute.
There is no string evaluation in SQL. This:
database.ID IN (' + #PassedInIDs + ')
will not be turned to:
database.ID IN (1,2,3)
just because the #PassedInIDs parameter happens to contain '1,2,3'. The parameter is not even looked at, because all you have is a string containing " + #PassedInIDs + ". Syntactically, this is equivalent to:
database.ID IN ('Bob')
To make it short, you can't do what you attempt here in SQL. But there are four other possibilities:
you construct the SQL string in the calling language and abandon the stored procedure altogether
you use a dynamic prepared statement with as many parameters in the IN clause as you pan to use
you use a fixed prepared statement with, say, 10 parameters: IN (?,?,?,?,?,?,?,?,?,?), filling only as many as you need, setting the others to NULL
you create a stored procedure with, say, 10 parameters and pass in as many as you need, setting the others to NULL: IN (#p1, #p2, ..., #p10).
I would create a CLR table-valued function:
http://msdn.microsoft.com/en-us/library/ms131103.aspx
In it, you would parse the string apart and perform a conversion to a set of rows. You can then join on the results of that table, or use IN to see if an id is in the list.
You need to treat ufn_CSVToTable like it's a table. So you can join the function:
JOIN ufn_CSVToTable(#PassedInIDs) uf ON database.ID = uf.[String]
I suggest using XML for this in SQL 2005. Somewhat bulkier, but it can be easier. It allows you to select the XML into a table which can then be joined or inserted etc.
Look at Sql Server's OPENXML() if you haven't already.
For example, you could pass in something like:
'12...'
and then use:
exec sp_xml_preparedocument #doc OUTPUT, #xmlParam
SELECT element
FROM OPENXML (#doc, 'Array/Value', 2) WITH (element varchar(max) 'text()')
That should be a start
this may be solved by 6 ways as mentioned in Narayana's article Passing a list/array to an SQL Server stored procedure
And my most strait forward implementation is
declare #statement nvarchar(256)
set #statement = 'select * from Persons where Persons.id in ('+ #PassedInIDs +')'
exec sp_executesql #statement
-
Here is what I have found and tested:
SET QUOTED_IDENTIFIER ON
SET ANSI_NULLS ON
GO
CREATE FUNCTION [dbo].[SplitStrings] ( #IDsList VARCHAR(MAX) )
RETURNS #IDsTable TABLE ( [ID] VARCHAR(MAX) )
AS
BEGIN
DECLARE #ID VARCHAR(MAX)
DECLARE #Pos VARCHAR(MAX)
SET #IDsList = LTRIM(RTRIM(#IDsList)) + ','
SET #Pos = CHARINDEX(',', #IDsList, 1)
IF REPLACE(#IDsList, ',', '') <> ''
BEGIN
WHILE #Pos > 0
BEGIN
SET #ID = LTRIM(RTRIM(LEFT(#IDsList, #Pos - 1)))
IF #ID <> ''
BEGIN
INSERT INTO #IDsTable
( [ID] )
VALUES ( CAST(#ID AS VARCHAR) )
END
SET #IDsList = RIGHT(#IDsList, LEN(#IDsList) - #Pos)
SET #Pos = CHARINDEX(',', #IDsList, 1)
END
END
RETURN
END
GO
Here is how function Call:
SELECT * FROM dbo.SplitStrings('123,548,198,547,965')
Try this:
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)+',%';

Creating a long string from a result set

I have a result set in MS-SQL within a stored procedure, and lets say it has one VARCHAR column, but many rows. I want to create a comma separated string conataining all these values, Is there an easy way of doing this, or am I going to have to step through each result and build the string up manually?
Preferably I'd like to do this in the Stored Procedure itself.
Here is one way (using AdventureWorks2008 DB):
DECLARE #name varchar(255)
SET #name = NULL
select #Name = COALESCE(#Name + ',','') + LastName from Person.Person
Select #name
And here is another (for SQL 2005 onwards):
SELECT
LastName + ','
FROM
Person.Person
FOR XML PATH('')
In both cases you will need to remove the trailing comma ',' (can use STUFF() function)