Building dynamic T-SQL query from a string argument in a sproc - sql

Let's say I have a table which contains a varchar field:
CREATE TABLE [MyTable] (
[MyId] varchar(3) NOT NULL,
.....
)
The [MyId] column contains sequential alphanum values like A1, A2... A99, B1, B2..B99, C1 and so on (up to Z99).
What I'd like to do is to extract rows from the table whose MyId field matches some specific prefixes... e.g. I'd like to fetch rows from the series A, C, P and X.
And I'd like to this with a sproc which will dynamically construct the query based on the prefix alphabets supplied in the argument.
I'm thinking about something like this...
CREATE PROCEDURE [dbo].[uspFilterMyTable]
#prefixArray varchar(max)
AS
... -- split individual characters from #prefixArray into an array
SELECT * FROM [MyTable]
WHERE
[MyId] LIKE ....
OR
[MyId] LIKE .... -- iterate all characters from #prefixArray
I think the main bulk of the stored procedure will resemble the following pseudo-code:
DECLARE #sql nvarchar(max)
-- iterate through all the characters
SET #sql = 'SELECT * FROM [MyTable] WHERE [MyId] LIKE ' + #charInTheArray + '%'
SET #sql = #sql + ' OR [MyId] LIKE ' + #nextCharInArray + '%'
EXEC (#sql)
The above proecedure will be called like this:
EXEC uspFilterMyTable("A,C,P,X")
... or perhaps like this (if it makes splitting the alphabets easier):
EXEC uspFilterMyTable("ACPX")
Any ideas? Pointers?
Update: OK, this is what I've come up with ([Split] function borrowed from Chhatrapati Sharma):
-- [MyTable] contains these rows: 'A7', 'A87', 'B16', 'C51', 'H99', 'X12'
-- the "input" parameter
DECLARE #prefixArray NVARCHAR(100)= 'H,A,C'
-- split the string into SQL wild-card patterns
DECLARE charCursor CURSOR FOR
select items + N'%' from dbo.Split(#prefixArray, ',')
OPEN charCursor;
DECLARE #pattern CHAR(2)
-- create temp table if necessary
IF NOT EXISTS(SELECT * FROM TEMPDB.SYS.TABLES WHERE NAME LIKE '#tmpTable%')
CREATE TABLE #tmpTable ([Id] VARCHAR(3) NOT NULL)
-- purge old data
DELETE FROM #tmpTable
FETCH NEXT FROM charCursor into #pattern
WHILE ##FETCH_STATUS = 0
BEGIN
--SELECT * INTO #tmpTable FROM [MyTable] WHERE [MyId] LIKE #pattern
Insert Into #tmpTable Select * FROM [MyTable] WHERE [MyId] LIKE #pattern
FETCH NEXT FROM charCursor into #pattern
END
CLOSE charCursor;
DEALLOCATE charCursor;
-- return the values
SELECT * FROM #tmpTable
It's ugly I know, but it works... any tips to improvise the code?

first you should create below function and then use this in query like this
SELECT * FROM [MyTable] WHERE [MyId] in (select items from dbo.split(#prefixArray,','))
CREATE FUNCTION [dbo].[Split](#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(#slice)
set #String = right(#String,len(#String) - #idx)
if len(#String) = 0 break
end
return
end

Here you have a nice and fast split method based on XML:
DECLARE #str NVARCHAR(100)= 'A1,B3,C4,B12,K19', #separator VARCHAR(1)= ','
DECLARE #SplitedList TABLE (code NVARCHAR(30))
DECLARE #XMLList XML
SET #XMLList=CAST('<i>'+REPLACE(#str, #separator,'</i><i>')+'</i>' AS XML)
INSERT INTO #SplitedList
SELECT x.i.value('(./text())[1]','varchar(100)')
FROM #XMLList.nodes('i') x(i)
SELECT * FROM #SplitedList
Result will be a table with the splitted values:
code
A1
B3
C4
B12
K19
From here you can continue and use this table on your procedure and join with you original table using LIKE as you propossed.

I would have suggested you to use table valued parameters to call your stored procedure. I guess you call it from .net. But EF I think will not be able to handle it, though you might check it. If not, I think the best way is to first parse the string into a temporary table, or a table value and after that join with it.
With TVP:
CREATE PROCEDURE [dbo].[uspFilterMyTable]
#prefixArray tvp_idlist readonly
as
select
t.*
from MyTable t
join #prefixArray pa on pa.id = t.myid
With a split function (of your choosing, you find many examples on the net)
CREATE PROCEDURE [dbo].[uspFilterMyTable]
#prefixArray varchar(max)
as
create #prefixArray tvp_idlist
insert into #prefixArray (id)
select id from dbo.myCustomSplit(#prefixArray,',')
select
t.*
from MyTable t
join #prefixArray pa on pa.id = t.myid
Where for both cases #prefixArray is a table variable is Id = varchar(3)
As an edit, after a little digging, it seems that with a little work EF works fine with TVPs. Check this : Entity Framework Stored Procedure Table Value Parameter. So The best thing is to send directly a table to your stored procedure, then to send a string to parse.

Related

How to exclude one column from result? [duplicate]

This question already has answers here:
Exclude a column using SELECT * [except columnA] FROM tableA?
(47 answers)
Closed 7 years ago.
I have a table with columns
a,b,c,d,e,f,g,...,z
Is it possible to write a simple query that will gives all columns except a without manualy specifing all other columns?
something that will be equivelant to:
Select b,c,d,e,f,g,h,...z
from TableG
To answer your question, you cannot do that directly, HOWEVER i found a solution for you.
The SELECT statement of SQL when using Physical table can only do SELECT * that will return all columns and SELECT Column1, Column2, Column3... for specific columns, there is no WHERE condition that will exclude 1 column name in the SELECT statement of SQL. How ever you can manipulate the table and the data the way you wanted it using temporary table
Solution:
Insert into temp table
Drop the column that you want to exclude from the temp table
Select all data from temp table.
SELECT * INTO #TempTable
FROM TableG
ALTER TABLE #TempTable
DROP COLUMN a
SELECT * FROM #TempTable
DROP TABLE #TempTable
I found the solution here: SQL exclude a column using SELECT * [except columnA] FROM tableA?
/************************************************************
Function To Split Strings
************************************************************/
SET QUOTED_IDENTIFIER ON
GO
IF OBJECT_ID('dbo.SplitString') IS NOT NULL
BEGIN
DROP FUNCTION dbo.SplitString
END
GO
create function [dbo].[SplitString]
(
#String varchar(max),
#Delimiter char(1)
)
returns #SplittedValues table
(
str_item varchar(100) primary key
)
as
begin
declare #SplitLength int
while len(#String) > 0
begin
select #SplitLength = (case charindex(#Delimiter,#String) when 0 then
len(#String) else charindex(#Delimiter,#String) -1 end)
insert into #SplittedValues
select substring(#String,1,#SplitLength)
select #String = (case (len(#String) - #SplitLength) when 0 then ''
else right(#String, len(#String) - #SplitLength - 1) end)
end
return
end
GO
/************************************************************
Function To Get columns names excluding some of them
************************************************************/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
IF OBJECT_ID('dbo.get_columns') IS NOT NULL
BEGIN
DROP FUNCTION dbo.get_columns
END
GO
CREATE FUNCTION dbo.get_columns
(
#table_name varchar(100),
#excluded_column_names varchar(100),
#delimter char(1)
)
RETURNS varchar(4000)
AS
BEGIN
declare #cols varchar(4000)
select #cols = COALESCE(#cols+',' ,'') + name
from sys.columns
where object_id=object_id(#table_name)
and name not in (select str_item from dbo.SplitString(#excluded_column_names,#delimter))
return #cols
END
GO
/************************************************************
Function To Get columns names excluding some of them
************************************************************/
declare #qry nvarchar(max)
set #qry = ' select ' + dbo.get_columns('TableName','Exclude_col_1,Exclude_col_2,Exclude_col_3',',')
+ ' from TableName'
+ ' where condition'
EXECUTE sp_executesql #qry
GO

SQL Server: How to achieve re-usability yet flexibility in TSQL

I am using SQL Server 2008 R2. I am having some problems finding an effective coding pattern for SQL which supports code re-usability as well as flexibility. By re-usability, what I mean is keeping SQL queries in Stored Procedures and User Defined Functions.
Now, if I choose Stored Procedures, I will be sacrificing its usability in a query directly. If I choose User Defined Functions, I won't be able to use DML statements.
For example, suppose I created a Stored Procedures which inserts one contact record. Now, if I am having a table which can act as a source of multiple contact records, all I am left with are either WHILE loops or CURSORs, which is clearly not a recommended option, due to its performance drawbacks. And due to the fact that DML statements are not allowed in User Defined Functions, I simply cannot use them for this purpose.
Although, If I am not concerned with code re-usability, then instead of using Stored Procedures I can surely use same set of queries again and again to avoid while loops.
What pattern should I follow?
Here is a similar Stored Procedures:-
ALTER Proc [dbo].[InsertTranslationForCategory]
(
#str nvarchar(max),
#EventId int,
#CategoryName NVarchar(500),
#LanguageId int,
#DBCmdResponseCode Int Output,
#KeyIds nvarchar(max) Output
)as
BEGIN
DECLARE #XmlData XML
DECLARE #SystemCategoryId Int
DECLARE #CategoryId Int
Declare #Counter int=1
Declare #tempCount Int
Declare #IsExists int
Declare #TranslationToUpdate NVarchar(500)
Declare #EventName Varchar(200)
declare #Locale nvarchar(10)
declare #Code nvarchar(50)
declare #KeyName nvarchar(200)
declare #KeyValue nvarchar(500)
select #Locale=locale from languages where languageid = #LanguageId
SET #DBCmdResponseCode = 0
SET #KeyIds = ''
select #EventName = eventName from eventLanguages
where eventID = #EventId
--BEGIN TRY
Select #SystemCategoryId=CategoryId from SystemCategories where Name=rtrim(ltrim(#CategoryName))
Select #CategoryId=CategoryId from Categories where Name=rtrim(ltrim(#CategoryName)) and EventId=#EventId
if (#str='deactivate')
Begin
Delete from Codetranslation where CategoryId=#CategoryId
Update Categories set [Status]=0, Isfilter=0 where CategoryId=#CategoryId and Eventid=#EventId
Set #DBCmdResponseCode=2
return
End
set #XmlData=cast(#str as xml)
DECLARE #temp TABLE
(
Id int IDENTITY(1,1),
Code varchar(100),
Translation varchar(500),
CategoryId int
)
Insert into #temp (Code,Translation,CategoryId)
SELECT
tab.col.value('#Code', 'varchar(200)'),
tab.col.value('#Translation', 'varchar(500)'),#SystemCategoryId
FROM #XmlData.nodes('/Data') AS tab (col)
select #tempCount=Count(*) from #temp
if(IsNull(#CategoryId,0)>0)
Begin
While (#Counter <= #tempCount)
Begin
Select #IsExists= count(sc.categoryid) from #temp t Inner Join SystemCodetranslation sc
On sc.categoryid=t.CategoryId
where ltrim(rtrim(sc.code))=ltrim(rtrim(t.code)) and ltrim(rtrim(sc.ShortTranslation))=ltrim(rtrim(t.Translation))
and t.Id= #Counter
print #IsExists
Select #Code = Code , #KeyValue = Translation from #temp where id=#counter
set #KeyName = ltrim(rtrim(#EventName)) + '_' + ltrim(rtrim(#CategoryName)) + '_' + ltrim(rtrim(#Code)) + '_LT'
exec dbo.AddUpdateKeyValue #EventId,#Locale, #KeyName,#KeyValue,NULL,12
select #KeyIds = #KeyIds + convert(varchar(50),keyvalueId) + ',' from dbo.KeyValues
where eventid = #EventId and keyname = #KeyName and locale = #Locale
set #KeyName = ''
set #KeyValue = ''
Set #Counter= #Counter + 1
Set #IsExists=0
End
End
--- Inser data in Codetranslation table
if(isnull(#CategoryId,0)>0)
Begin
print #CategoryId
Delete from codetranslation where categoryid=#CategoryId
Insert into codetranslation (CategoryId,Code,LanguageId,ShortTranslation,LongTranslation,SortOrder)
SELECT
#CategoryId,
tab.col.value('#Code', 'varchar(200)'), #LanguageId,
tab.col.value('#Translation', 'varchar(500)'),
tab.col.value('#Translation', 'varchar(500)'),0
FROM #XmlData.nodes('/Data') AS tab (col)
Update Categories set [Status]=1 where CategoryId=#CategoryId and Eventid=#EventId
End
Set #DBCmdResponseCode=1
set #KeyIds = left(#KeyIds,len(#KeyIds)-1)
END
You can use table variable parameter for your user defined functions.
following code is an example of using table variable parameter in stored procedure.
CREATE TYPE IdList AS TABLE (Id INT)
CREATE PROCEDURE test
#Ids dbo.IdList READONLY
AS
Select *
From YourTable
Where YourTable.Id in (Select Id From #Ids)
End
GO
In order to execute your stored procedure use following format:
Declare #Ids dbo.IdList
Insert into #Ids(Id) values(1),(2),(3)
Execute dbo.test #Ids
Edit
In order to return Inserted Id, I don't use from Table Variable Parameter. I use following query sample for this purpose.
--CREATE TYPE NameList AS TABLE (Name NVarChar(100))
CREATE PROCEDURE test
#Names dbo.NameList READONLY
AS
Declare #T Table(Id Int)
Insert Into YourTable (Name)
OUTPUT Inserted.Id Into #T
Select Name
From #Names
Select * From #T
End
GO

SQL like for comma separated input

I want to write a stored procedure in which I want to run a query for multiple input which comes as a comma separated string. Just like we have in for exact match, can I have something like in too?
Input:
51094,51096,512584
Attempting to do:
select * from table where column like ('%51094%','%51096%','%512584%')
My query should iterate through each input and get the column which matches the pattern.
I have already tried following:
Contains(Column, '"*51094*" or "*51096*" or "*512584*")
But can't configure freetext search now.
Source: Is there a combination of "LIKE" and "IN" in SQL?
All the proposed types in: How to use SQL LIKE condition with multiple values in PostgreSQL?
None seems to be working.
Please suggest a simple way.
Try with first explode your input
$arr = explode($Input,",");
column like "%".$arr[0]."%" OR
column like "%".$arr[1]."%" OR
column like "%".$arr[2]."%"
This function you can use, no any mandatory to give comma only you can give special character.
ALTER function [dbo].[SplitString] (#String nvarchar(4000), #Delimiter char(1))
Returns #Results Table (Items nvarchar(50))
As
Begin
Declare #Index int
Declare #name nvarchar(20)
Declare #Slice nvarchar(50)
Select #Index = 1
If #String Is NULL Return
While #Index != 0
Begin
Select #Index = CharIndex(#Delimiter, #String)
If #Index <> 0
Select #Slice = left(#String, #Index - 1)
else
Select #Slice = #String
Insert into #Results(Items) Values (#Slice)
Select #String = right(#String, Len(#String) - #Index)
If Len(#String) = 0 break
End
Return
End
Looped the items and got it done.
Select * into #temp_inputIds from dbo.Split(#InputIds,',')
DECLARE #ID varchar (50)
DECLARE IDs CURSOR LOCAL FOR select items from #temp_inputIds
OPEN IDs
FETCH NEXT FROM IDs into #ID
WHILE ##FETCH_STATUS = 0
BEGIN
Select #SQL = 'Select component_id,'+#ID+' as pub_id from component_presentations where CONTENT like ''%' + #ID + '%'''
FETCH NEXT FROM IDs into #ID
END
CLOSE IDs
DEALLOCATE IDs

Loops within dynamic SQL

I have code that I'd like to apply to a number of tables but rather than simply copy and replace table names, I'd like to use some kind of loop or cursor to simplify things.
I envision setting up an array of my tables names and using an index to iterate over the list, retrieving each table name and using dynamic SQL to intersperse the table name where applicable in my code.
Since there's no 'array' construct, as far as I know, within SQL, I'm not sure how this would work.
Any ideas about how to go about this?
Here is one way of doing it:
--Declare a table variable to hold your table names (and column names in case needed)
declare #listOfTablesToUpdate table (tableName varchar(100), columnNameToUpdate varchar(50))
--insert the tables that you want to work with.
insert into #listOfTablesToUpdate values ('Table1', 'column2')
insert into #listOfTablesToUpdate values ('Table2', 'column3')
insert into #listOfTablesToUpdate values ('Table3', 'column4')
--Cursor for iterating
declare #tableCursor cursor,
#tableName varchar(100),
#columnName varchar(50)
set #tableCursor = cursor for select * from #listOfTablesToUpdate
open #tableCursor
fetch next from #tableCursor into #tableName, #columnName
while(##fetch_status = 0)
begin
--dynamic sql
declare #sql varchar(max)
--Your logic here...this is just an example
set #sql = 'update '+#tableName+' set '+#columnName+' = '+<value>+' where '+#columnName +' = '+<someothervalue>
exec #sql
fetch next from #tableCursor into #tableName, #columnName
end
close #tableCursor
deallocate #tableCursor
Another approach involves preparing a helper function and a procedure that allow one to apply different SQL statements to each object (table, database, et cetera) in a list. The helper function comes from a SSRS Parameter question and splits apart a comma delimited list into a table.
-- from https://stackoverflow.com/questions/512105/passing-multiple-values-for-a-single-parameter-in-reporting-services
CREATE FUNCTION [dbo].[fn_MVParam]
(#RepParam NVARCHAR(4000), #Delim CHAR(1)= ',')
RETURNS #Values TABLE (Param NVARCHAR(4000))AS
BEGIN
DECLARE #chrind INT
DECLARE #Piece NVARCHAR(100)
SELECT #chrind = 1
WHILE #chrind > 0
BEGIN
SELECT #chrind = CHARINDEX(#Delim,#RepParam)
IF #chrind > 0
SELECT #Piece = LEFT(#RepParam,#chrind - 1)
ELSE
SELECT #Piece = #RepParam
INSERT #Values(Param) VALUES(CAST(#Piece AS VARCHAR))
SELECT #RepParam = RIGHT(#RepParam,LEN(#RepParam) - #chrind)
IF LEN(#RepParam) = 0 BREAK
END
RETURN
END
GO
Below is the code for the ProcessListSQL procedure.
-- #SQL to execute shall include {RP} as the replacement expression that
-- will evaluate to all the items in the comma delimited list
-- Also, please include a double quote " rather than two single quotes ''
-- in the input statement.
CREATE PROCEDURE [dbo].[ProcessListSQL] (
#CommaDelimitedList AS NVARCHAR(MAX),
#SQLtoExecute AS NVARCHAR(MAX) )
AS BEGIN
DECLARE #Statements TABLE
( PK INT IDENTITY(1,1) PRIMARY KEY,
SQLObject NVARCHAR (MAX)
)
SET #SQLtoExecute = REPLACE (#SQLtoExecute, '"', '''')
INSERT INTO #Statements
SELECT PARAM FROM [dbo].[fn_MVParam](#CommaDelimitedList,',')
DECLARE #i INT
SELECT #i = MIN(PK) FROM #Statements
DECLARE #max INT
SELECT #max = MAX(PK) FROM #Statements
DECLARE #SQL AS NVARCHAR(MAX) = NULL
DECLARE #Object AS NVARCHAR(MAX) = NULL
WHILE #i <= #max
BEGIN
SELECT #Object = SQLObject FROM #Statements WHERE PK = #i
SET #SQL = REPLACE(#SQLtoExecute, '{RP}', #Object)
-- Uncommend below to check the SQL
-- PRINT #SQL
EXECUTE sp_executesql #SQL
SELECT #Object = NULL
SELECT #SQL = NULL
SET #i = #i + 1
END
END
GO
The ProcessListSQL procedure take two parameters. The first is a comma delimited string that contains the list of objects that will be cycled through. The second parameter is a string that contains the SQL that will be executed with each of the objects in the first parameter.
In the below example, four databases are created. Note that {rp} is replaced with each of the objects in the first parameter and double quotes are needed in each place where single quotes are needed in the SQL statement.
EXECUTE ProcessListSQL 'rice,apples,cheese,tomatos',
'CREATE DATABASE [{rp}] CONTAINMENT = NONE
ON PRIMARY ( NAME = N"{rp}",
FILENAME = N"D:\data\user\{rp}.mdf" ,
SIZE = 4096KB ,
FILEGROWTH = 1024KB )
LOG ON
( NAME = N"{rp}_log",
FILENAME = N"D:\DATA\USER\{rp}_log.ldf" ,
SIZE = 1024KB ,
FILEGROWTH = 10%)'

SQL : in clause in stored procedure:how to pass values

I want to write a SQL Server 2005 stored procedure which will select and return the user records from the user table for some userids which are passed to the stored procedure as parameter.
How to do this ?
I can pass the user ids as a string separated by comma. So that I can use the
select *
from users
where userid in (userids)
E.g. : I want to select records for id's 5,6,7,8,9
How to write the stored procedure ?
For SQL Server 2005, check out Erland Sommarskog's excellent Arrays and Lists in SQL Server 2005 article which shows some techniques how to deal with lists and arrays in SQL Server 2005 (he also has another article for SQL Server 2000).
If you could upgrade to SQL Server 2008, you can use the new feature called "table valued parameter":
First, create a user-defined table type
CREATE TYPE dbo.MyUserIDs AS TABLE (UserID INT NOT NULL)
Secondly, use that table type in your stored procedure as a parameter:
CREATE PROC proc_GetUsers #UserIDTable MyUserIDs READONLY
AS
SELECT * FROM dbo.Users
WHERE userid IN (SELECT UserID FROM #UserIDTable)
See details here.
Marc
Just use it like this will work
Create procedure sp_DoctorList
#userid varchar(100)
as
begin
exec ('select * from doctor where userid in ( '+ #userid +' )')
end
you could use dynamic sql. Pass the in statement to a Sql SP via a variable and concatenate it into a query in the SQL and execute using sp_execute sql
create procedure myproc(#clause varchar(100)) as
begin
exec sp_executesql 'select * from users where userid in ( ' + #clause +' )'
end
see my previous answer to this
this is the best source:
http://www.sommarskog.se/arrays-in-sql.html
create a split function, and use it like:
SELECT
*
FROM YourTable y
INNER JOIN dbo.splitFunction(#Parameter) s ON y.ID=s.Value
I prefer the number table approach
For this method to work, you need to do this one time table setup:
SELECT TOP 10000 IDENTITY(int,1,1) AS Number
INTO Numbers
FROM sys.objects s1
CROSS JOIN sys.objects s2
ALTER TABLE Numbers ADD CONSTRAINT PK_Numbers PRIMARY KEY CLUSTERED (Number)
Once the Numbers table is set up, create this function:
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 TABLE
AS
RETURN
(
----------------
--SINGLE QUERY-- --this will not return empty rows
----------------
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!=''
);
GO
You can now easily split a CSV string into a table and join on it:
select * from dbo.FN_ListToTable(',','1,2,3,,,4,5,6777,,,')
OUTPUT:
ListValue
-----------------------
1
2
3
4
5
6777
(6 row(s) affected)
Your can pass in a CSV string into a procedure and process only rows for the given IDs:
SELECT
y.*
FROM YourTable y
INNER JOIN dbo.FN_ListToTable(',',#GivenCSV) s ON y.ID=s.ListValue
Assuming T-SQL, you can use this nice function (that returns a table).
DROP FUNCTION sp_ConvertStringToTable
GO
CREATE FUNCTION sp_ConvertStringToTable(#list ntext)
RETURNS #tbl TABLE (Position INT IDENTITY(1, 1) NOT NULL,
Value INT NOT NULL) AS
BEGIN
DECLARE #pos int,
#textpos int,
#chunklen smallint,
#str nvarchar(4000),
#tmpstr nvarchar(4000),
#leftover nvarchar(4000)
SET #textpos = 1
SET #leftover = ''
WHILE #textpos <= datalength(#list) / 2
BEGIN
SET #chunklen = 4000 - datalength(#leftover) / 2
SET #tmpstr = ltrim(#leftover + substring(#list, #textpos, #chunklen))
SET #textpos = #textpos + #chunklen
SET #pos = charindex(' ', #tmpstr)
WHILE #pos > 0
BEGIN
SET #str = substring(#tmpstr, 1, #pos - 1)
INSERT #tbl (Value) VALUES(convert(int, #str))
SET #tmpstr = ltrim(substring(#tmpstr, #pos + 1, len(#tmpstr)))
SET #pos = charindex(' ', #tmpstr)
END
SET #leftover = #tmpstr
END
IF ltrim(rtrim(#leftover)) <> ''
INSERT #tbl (Value) VALUES(convert(int, #leftover))
RETURN
END
GO
In this way:
SELECT * FROM Users
WHERE userid IN
( SELECT Value FROM sp_ConvertStringToTable('1 2 3') )
You can change the stored function to work with comma separated strings instead of space separated ones.
If you don't want / can't use a stored function you can include the code of it inside the stored procedure where needed.
EDIT: this is incredibly more performant than the string concatenation.
try this this works for me
DECLARE #InClause NVARCHAR(100)
SET #InClause = 'tom,dick,harry'
DECLARE #SafeInClause NVARCHAR(100)
SET #SafeInClause = ',' + #InClause + ','
SELECT * FROM myTable WHERE PATINDEX(',' + myColumn + ',', #SafeInClause) > 0
Quick and dirty..
CREATE PROCEDURE SelectUsers (#UserIds VARCHAR(8000))
AS
SELECT * FROM Users
WHERE userid IN (SELECT CONVERT(VARCHAR(8000), value) FROM STRING_SPLIT(#UserIds, ','))
EXEC SelectUsers #UserIds = 'a1b2,c3d4,e5f6'
You can also use Find_IN_SET instead of IN. See the query below
create procedure myproc(IN in_user_ids varchar(100))
begin
select * from users where FIND_IN_SET(userid, in_user_ids);
end