How to use a variable inside IN operator [duplicate] - c#

This question already has answers here:
Define variable to use with IN operator (T-SQL)
(16 answers)
Multiple Id's in In clause of SQL Query C# [closed]
(2 answers)
Closed 5 years ago.
I'm trying to write an application in C# that will pass a variable that contains multiple value separated by a comma, to a stored procedure
string state = "AZ, CA, VA"
I want my stored procedure to return all data for each state in the variable
CREATE PROCEDURE usp_GetStateInfo
#state varchar(2)
SELECT * FROM StateTable WHERE state IN (#state)
But when I do the code above I get an error saying 'Incorrect syntax'
What am I doing wrong and how can I fix this?
Thank you

You cannot use multiple inputs as parameter for IN statement. You will have to split those comma separated values in SQL to create a table variable and join with that table variable.
You can create a function for splitting input string :-
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[udf_PutStringtoTableUnique]') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT'))
DROP FUNCTION [dbo].[udf_PutStringtoTableUnique]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE function [dbo].[udf_PutStringtoTableUnique]
(
#inputStr as varchar(max),
#delim nchar(1) = N','
)
returns #tbl table(ID int primary key)
as
BEGIN
if #inputStr = '' or #inputStr is null
return
declare #tags nchar(2)
set #tags = #delim + #delim;
with tbl_for_csv as
(
select left(#inputStr + #tags,charindex(#delim,#inputStr + #tags) -1)as Col,
right(#inputStr + #tags,len(#inputStr + #tags) - charindex(#delim,#inputStr + #tags)) as Str
union all
select left(Str,charindex(#delim,Str) - 1)as Col,
right(Str,len(Str) - charindex(#delim,Str)) from tbl_for_csv
where len(right(Str,len(Str) - charindex(#delim,Str))) > 0
)
insert into #tbl
select distinct Col from tbl_for_csv
option (maxrecursion 0)
return
END
GO
SET ANSI_NULLS OFF
GO
SET QUOTED_IDENTIFIER ON
GO
And Then write something like this in procedure :-
DECLARE #TempStates Table (StateName Varchar(2))
INSERT INTO #TempStates
Select id from [dbo].[udf_PutStringtoTableUnique](#state, ',')
SELECT * FROM StateTable
INNER JOIN #TempStates temp ON state = temp.StateName

The IN operator accepts an inline list of values, or a select statement. There is no native list type of variable in SQL. And certainly varchar(2) will not work, as you have it.
As you are using C#, it makes sense to loop through a list of states, calling the stored procedure for each element. The procedure would use = instead of IN to filter the states.
If you insist that the procedure accept a string of comma-separated values, then you would have to use SQL commands to split the string and insert each value into a temp table or table variable, to then have a select statement for the INto consume.
In T-SQL, you can do this with STRING_SPLIT() for SQL Server 2016
https://learn.microsoft.com/en-us/sql/t-sql/functions/string-split-transact-sql
SELECT *
FROM StateTable
WHERE state IN(
SELECT value
FROM STRING_SPLIT(#states, ',') )

Related

How to accept a list into an SQL TableAdapter query using C#?

I'd like to perform an SQL query on a data table and return records with IDs that match any one of the IDs in a particular list (which would be passed in as a parameter to the SQL TableAdapter query). This ID which is being checked is a foreign key in the table. The following is the code am I using, only including what is relevant:
SELECT BookingID, DogID
FROM Booking
WHERE DogID IN (...)
Ideally, I'd like to be able to pass in a list of type string.
You can pass id as , separated string and use User Define function to split that:
try below:
UDF
CREATE FUNCTION [dbo].[Udf_stringtolist] (#List NVARCHAR(max),
#Delimeter NVARCHAR(10))
returns #tmpListTable TABLE (
value NVARCHAR(max))
AS
BEGIN
DECLARE #DelimPosition INT
SELECT #DelimPosition = Charindex(#Delimeter, #List)
WHILE #DelimPosition > 0
BEGIN
INSERT INTO #tmpListTable
(value)
VALUES (Cast(Ltrim(Rtrim(LEFT(#List, #DelimPosition - 1))) AS
VARCHAR(
100)))
SELECT #List = RIGHT(#List, Len(#List) - #DelimPosition),
#DelimPosition = Charindex(#Delimeter, #List)
END
IF Len(#List) > 0
INSERT INTO #tmpListTable
(value)
VALUES (Cast(Ltrim(Rtrim(#List)) AS VARCHAR(100)))
RETURN
END
Use the function:
DECLARE #idList NVARCHAR(max)=',1,2,3,4,5,6';
SELECT bookingid,
dogid
FROM booking
WHERE dogid IN (SELECT *
FROM [dbo].[Udf_stringtolist] (#idList, ',')
ORDER BY 1)
There are 2 ways.
Comma separated ids - which will lead to performance issue while split it
XML - is best approach.

passing my comma separated string won't generate results in SQL [duplicate]

This question already has answers here:
Parse comma-separated string to make IN List of strings in the Where clause
(4 answers)
Closed 7 years ago.
I Have a need to pass a comma delimited string as a parameter to a stored procedure. That string contains ints seperated by commas. Then I need to use the ints in an IN clause, like,
"where ReferenceValue in
(
'50022056',
'50022073',
'50022075',
'50022072',
'50022086',
'50022095',
'50022074',
'50022077',
'50022100',
'50022101',
'50022106',
'50022107',
'50022108',
'50022109',
'50022110',
'50022111',
'50022112',
'50022113',
'50022115',
'50022116',
'50022117',
'50022118',
'50022119'
)"
Now I have a parameter I am passing to a stored proc containing string parameter ="50001362,50001414,50001437,50001630,50001643,50001659,50001775,50001272,50001276,50001220,500012226"
I have tried using Dynamic SQL and doing
where ReferenceValue in
(
'+#groupNumbers+'
)
however, i get back no results
At the top of my stored proc i am declaring the variable
DECLARE #groupNumbers VarChar(1000)
SET #groupNumbers = '50008300,50002427'
The SET is just a test case to see how I can get back results with a comma delimited string. I need to pass in my string of group numbers into this parameter and get back results. Currently i Am looping through each group number and calling the query with each group number.
Any advice on what i am doing wrong? I am very novice at SQL.
Thank you
Please refer to my answer in this other SO post How to Convert a delimited string to a list
Then after you have decided to either use the CLR method or the SQL UDF method you can then do the following:
select *
from dbo.YourTableHere t
inner join dbo.fnArray(#groupNumbers, ',') g on t.somefield = convert(int, g.arrValue)
Should get you what you want.
base on solution described here Using-comma-separated-value-parameter-strings-in-S
/****** Object: UserDefinedFunction [dbo].[CSVToTable] Script Date: 04/28/2013 10:45:17 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION [dbo].[CSVToTable] (#InStr VARCHAR(MAX))
RETURNS #TempTab TABLE
(id int not null)
AS
BEGIN
;-- Ensure input ends with comma
SET #InStr = REPLACE(#InStr + ',', ',,', ',')
DECLARE #SP INT
DECLARE #VALUE VARCHAR(1000)
WHILE PATINDEX('%,%', #INSTR ) <> 0
BEGIN
SELECT #SP = PATINDEX('%,%',#INSTR)
SELECT #VALUE = LEFT(#INSTR , #SP - 1)
SELECT #INSTR = STUFF(#INSTR, 1, #SP, '')
INSERT INTO #TempTab(id) VALUES (#VALUE)
END
RETURN
END
GO
Simple:
DECLARE #LIST VARCHAR(200)
SET #LIST = '1,3'
SELECT Id, Descr FROM CSVDemo WHERE Id IN (SELECT * FROM dbo.CSVToTable(#LIST))

How can I supply a List<int> to a SQL parameter? [duplicate]

This question already has answers here:
Pass Array Parameter in SqlCommand
(11 answers)
Closed 6 years ago.
I have a SQL statement like the following:
...
const string sql = #"UPDATE PLATYPUS
SET DUCKBILLID = :NEWDUCKBILLID
WHERE PLATYPUSID IN (:ListOfInts)";
...
ocmd.Parameters.Add("ListOfInts", ??WhatNow??);
How can I provide the comma separated list of ints, which could be any (reasonable*) number of values
By "reasonable" in this case I mean between one and a couple dozen.
You can't, you have to create some helper function that replaces :ListOfInts with ( for example ) :I0,:I1,:I2... and pass the parameter by adding one by one in code. The function you want is gracefully emulated by Dapper.Net in its list support.
I think the only possible solution is to pass comma-separated values, which you can convert to a table in SQL using a function. Here is the function I am using
CREATE FUNCTION dbo.CSVToList (#CSV varchar(3000))
RETURNS #Result TABLE (Value varchar(30))
AS
BEGIN
DECLARE #List TABLE
(
Value varchar(30)
)
DECLARE
#Value varchar(30),
#Pos int
SET #CSV = LTRIM(RTRIM(#CSV))+ ','
SET #Pos = CHARINDEX(',', #CSV, 1)
IF REPLACE(#CSV, ',', '') <> ''
BEGIN
WHILE #Pos > 0
BEGIN
SET #Value = LTRIM(RTRIM(LEFT(#CSV, #Pos - 1)))
IF #Value <> ''
INSERT INTO #List (Value) VALUES (#Value)
SET #CSV = RIGHT(#CSV, LEN(#CSV) - #Pos)
SET #Pos = CHARINDEX(',', #CSV, 1)
END
END
INSERT #Result
SELECT
Value
FROM
#List
RETURN
END
and you can use the following code (for example) to perform operations:
DECLARE #CSV varchar(100)
SET #CSV = '30,32,34,36,40'
SELECT
ProductID,
ProductName,
UnitPrice
FROM
Products
WHERE
ProductID IN (SELECT * FROM dbo.CSVToLIst(#CSV))
I took the code from here: http://www.geekzilla.co.uk/view5C09B52C-4600-4B66-9DD7-DCE840D64CBD.htm
Hope it helps.
You don't state which database you are using, but if it is SQL Server 2008+ you may also want to consider Table Valued Parameters.
For your example the added complexity may not be worthwhile, but they can be useful in other scenarios.
The best way to handle this is to keep the list in the database, such that you could write a select query to return the values in your list. The question is, how can you do this when the data originates at the client? Most of the time, this data is hand-picked by the user, one record at a time. In that case, you can use a table (think: "shopping cart") that adds records for the values one at a time, as the user selects them. If it's more of a batch job, you may want to use a BulkInsert process to create the records.
Use the Join method of string on the Array of Ints. You can mention comma as the seperator
List<int> ints = new List<int>();
ints.Add(4);
ints.Add(6);
ints.Add(2);
string concatenatedIds= string.Join(",", ints.ToArray());
The output (value in concatenatedIds) will be 4,6,2. You can use that string as the parameter value for your IN clause
ocmd.Parameters.Add("ListOfInts",concatenatedIds);

SQL IN Clause In Stored Procedure

I have a C# app that passes in the following data:
datasetID = 10;
userID = 1;
varnames = "'ACT97','ACTCHNG','ACTQTR2','ACTSCOR2'";
The stored procedure is:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[s_LockCheck]
-- Add the parameters for the stored procedure here
#varNames VARCHAR(max),
#datasetID INT,
#userID INT
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON
SELECT COUNT(*) as locked FROM VarLocks WHERE var_name IN (#varNames) AND dataset_id = #datasetID AND user_id != #userID AND is_locked = 1
END
But when I call it like so, it is returning a count of 0 when it should be higher than that:
exec s_LockCheck "'ACT97','ACTCHNG','ACTQTR2','ACTSCOR2'", 88, 14
Each ACTXXX above is a varname from the column, var_name.
Why isn't it doing the IN Clause correctly?
There are several ways to accomplish this:
Dynamic SQL, as pointed out in this article: http://asheej.blogspot.com/2012/04/how-to-use-ms-sql-in-clause-with.html
Specify each item in variables (this can get quite ugly if you have a lot of 'em):
#var1 varchar(20),
#var2 varchar(20),
#var3 varchar(20)
Write a split function to turn the string into a table variable, there are many of them out there. This one is my personal favorite: http://dataeducation.com/faster-more-scalable-sqlclr-string-splitting/
Use a Table Value Parameter (2008): http://www.techrepublic.com/blog/datacenter/passing-table-valued-parameters-in-sql-server-2008/168
Here's a little trick using CHARINDEX (note that this approach is Non-Sargable):
Your string is like so: 'abc,def'
Using CHARINDEX, you pad both the search string and value you want to find within the search string with your delimeter. So using my little example, the string would become ',abc,def,' Notice the extra commas at the beginning and end. Then do the same thing to the field data. If you have commas in your data, you'll have to swap out the delimeter to something else, like char(2), or semi-colons, or whatever.
Then to perform the search:
WHERE CHARINDEX ( ',' + expressionToFind + ',' , ',' + expressionToSearch ',') > 0
The delimeter padding keeps the search from finding "abcabc" but will find "abc", exact match.
If you're using 2005, I'd grab a really fast split function so you can avoid using dynamic SQL.
Your query should use STRING_SPLIT for IN, like this:
SELECT COUNT(*) as locked FROM VarLocks
WHERE var_name
IN (SELECT value FROM STRING_SPLIT(#varNames, ','))
AND dataset_id = #datasetID
AND user_id != #userID
AND is_locked = 1
You had wrong syntax for IN operator: IN (#varNames)
and you needed IN (SELECT value FROM STRING_SPLIT(#varNames, ',')).
STRING_SPLIT performs string splitting according to delimiter, which is comma in your case.
pass comma separated string from c#
sql
WHERE Location IN (SELECT value FROM STRING_SPLIT(#SearchLocation,'|'))

Send a list of IDs to a SQL Server stored procedure from c#

Is it possible to send a list of IDs to a stored procedure from c#?
UPDATE Germs
SET Mutated = ~Mutated
WHERE (GermID IN (ids))
This may be a dirty hack, but you can create a temp table and then join to it from within your stored procedure (assuming they are accessed during the same connection). For example:
CREATE TABLE #ids (id int)
INSERT INTO #ids VALUES ('123') -- your C# code would generate all of the inserts
-- From within your stored procedure...
UPDATE g
SET Mutated = ~Mutated
FROM Germs g
JOIN #ids i ON g.GermID = i.id
You could try what i have made do with:-
Create a function called Split_String
set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
go
CREATE FUNCTION [dbo].[Split_String]
(
#MyString varchar(5000)
)
RETURNS #Results TABLE
(
Value varchar(1000)
)
AS
BEGIN
DECLARE #Pos int
DECLARE #StrLen int
DECLARE #MyLen int
DECLARE #MyVal varchar
SET #pos = 1
SET #MyLen = 1
WHILE #MyString <> ''
BEGIN
SET #MyLen = charindex(',',#MyString)
IF #MyLen = 0 SET #MyLen = Len(#MyString)
INSERT #Results SELECT replace(substring(#MyString, #pos, #MyLen),',','')
SET #MyString = SUBSTRING(#MyString,#MyLen+1,len(#MyString))
END
RETURN
END
Then when you use IN() use in the following fashion with a comma separated string:-
SELECT * FROM [youDataBase].[dbo].[Split_String] (<#MyString, varchar(5000),>)
According to This article, you could try the Table Value Parameter.
Yep, you can use a chunk of XML to build your list of ID's. Then you can use OPENXML and select from that record set.
Look up OPENXML, sp_preparexmldocument, sp_removexmldocument

Categories