Get parameters for a ADO.NET CommandText - c#

Is there any way (maybe even in ADO.NET) to get a list of the parameters of a CommandText?
That is to say, given the following query:
INSERT INTO TABLE test VALUES(#value1, #value2)
Is there any way to know that the query parameters are value1, value2? Or should I have to parse the query on my own to find them (of course '#' syntax may change from one db engine to other...)?
Clarification:
I want to know the parameters name and the order in the query before having to fill the command parameters dictionary.

You can use SqlParameterCollection.Count property to get that information.
So you can do like
SqlCommand cmd = new SqlCommand("insert into table test values(#value1, #value2)", con);
cmd.Parameters.Add("#value1", SqlDbType.VarChar).Value = "test";
cmd.Parameters.Add("#value2", SqlDbType.VarChar).Value = "test";
int count = cmd.Parameters.Count; // you get the parameter count
You as well get the name of parameter using the parameter collection like
string p1 = cmd.Parameters[0].ParameterName;
string p2 = cmd.Parameters[1].ParameterName;
Not exactly sure what you mean by parameters order in the query

If the query is a stored procedure, you can use SqlCommandBuilder.DeriveParameters (see How can I retrieve a list of parameters from a stored procedure in SQL Server). However that unfortunately only works for CommandType=StoredProcedure
In SQL Server 2012 (and above) you can use sp_describe_undeclared_parameters (see https://msdn.microsoft.com/en-us/library/ff878260.aspx)
Doesn't seem like there is any alternative for lower versions other than parsing the SQL query text directly (regex, or possibly using the SQLDOM that ships with SSDT (or via nuget): https://blogs.msdn.microsoft.com/arvindsh/2013/04/04/using-the-transactsql-scriptdom-parser-to-get-statement-counts/ )
Edit: simple F# example of finding parameters using the Microsoft.SqlServer.TransactSql.ScriptDom nuget:
#r "Microsoft.SqlServer.TransactSql.ScriptDom"
open System.IO
open System.Collections.Generic
open Microsoft.SqlServer.TransactSql.ScriptDom
let query = #"select * from dbo.Customers where FirstName=#FirstName"
let parser = new TSql120Parser(true)
let errors : ref<IList<ParseError>> = ref(null)
let frag = parser.Parse(new StringReader(query), errors) // the actual work
frag.ScriptTokenStream
|> Seq.filter (fun f -> f.TokenType = TSqlTokenType.Variable)
|> Seq.iter (fun t -> (printfn "Type = %A; Name = %s" t.TokenType t.Text))
outputs
Type = Variable; Name = #FirstName
...however if the script involves temporary variables you can easily get false positives, eg:
declare #temp nvarchar(50) = #FirstName
select * from dbo.Customers where FirstName=#temp
...finds #temp twice, and #FirstName

Related

How to see SQL command execution with Stored Procedure

I have the below block of code:
private TData ExecuteReturnData<TData>(string procName, Func<IDataReader, TData> translator, SqlConnection sqlCon, params SqlParameter[] parameters)
{
using var sqlCmd = CreateCommand(procName, sqlCon, parameters);
sqlCmd.CommandTimeout=120;
using var reader = sqlCmd.ExecuteReader();
var elems = translator(reader);
return elems
}
Here I want to see the complete SQL command execution with params in SQL Server code block. How can I check that here?
What you're asking for does not exist. It never exists at any point. The entire purpose of using parameterized queries is the parameter data is NEVER substituted directly into the SQL command string, and therefore will not be available to view in that way.
Parameterized queries are more than simply sanitizing or escaping in the parameter data in the proper way; they quarantine the data from the command, so the two can never meet.
That is, if you have this query:
SELECT * FROM Users WHERE FirstName= #FirstName
and this parameter value:
Samuel
instead of something like this:
SELECT * FROM Users WHERE FirstName = 'Samuel'
The parameter data is sent to the server in a completely separate block than the SQL command. The server receives both parts and does something more like this:
DECLARE #FirstName nvarchar(40) = LoadParameterFromClient()
SELECT * FROM Users WHERE FirstName= #FirstName
(Note: the actual mechanism for this is sp_executesql)
But for what it's worth, I tend to structure similar C# code more like this:
private IEnumerable<TData> ExecuteReturnData<TData>(string SQL, Func<IDataRecord, TData> translator, Action<SqlParameterCollection> addParams)
{
using var conn = new SqlConnection(" ... "); // My data layer knows about the database I'm using, so I don't need to pass in a conneciton
using var cmd = new SqlCommand(SQL, conn);
if (addParams is object) addParams(cmd.Parameters);
conn.Open();
using var reader = sqlCmd.ExecuteReader();
while (reader.Read())
{
yield return translator(reader);
}
}
Then I'd call it using a similar example as above like this:
var results = ExecuteReturnData<string>("SELECT FirstName, LastName FROM Users WHERE FirstName=#FirstName",
p => p.Add("#FirstName", SqlDbType.NVarchar,40).Value = "Samuel",
r => r["LastName"] + ", " + r["FirstName"]);
For more complex result types I'd have a static FromSQL(IDataRecord data) method on the target type, to avoid making this function call too difficult to read:
var results = ExecuteReturnData<User>("SELECT * FROM Users WHERE FirstName=#FirstName",
p => p.Add("#FirstName", SqlDbType.NVarchar,40).Value = "Samuel",
User.FromSQL);
As a project grows I might also collect these methods into a separate static type, to avoid over-coupling between the data layer and client code.
And of course you can run stored procedures the same way:
var results = ExecuteReturnData("exec MyProcedure #Param1, #Param2" ... );
How to see SQL command execution with Stored Procedure
I want to see the complete SQL command execution with params in SQL Server code block. How can I check that here?
In Visual Studio open the SQL Server Object Explorer > New Connection to SQL Server > Expand Database > Expand Programmatibility > Stored Procedures and Right Click on a Sproc and choose Debug Procedure. You can step though the T-SQL Code. But you can't jump into the T-SQL from the .Net Code which sounds like what you want.
Debug Procedure > Press F11 to Step into the Stored Procedure:
REF: https://learn.microsoft.com/en-us/sql/ssms/scripting/transact-sql-debugger?view=sql-server-ver16

Passing in Oracle Parameter to SQL string

I'm having a problem where I don't know how I'm supposed to pass in an Oracle parameter where the C# type is a string and the Oracle type is a Varchar2.
Currently I'm passing in this string as CMS','ABC thinking that Oracle will add in the '' that surround this string making it a varchar2 that looks like 'CMS','ABC'.
This works for a single string like CMS but when the value is something longer, like something typically in a IN (list) command the parameter won't be passed in correctly.
This is the code I'm referring too.
string sql = 'SELECT name FROM Pers p WHERE p.FirstName IN (:names)';
The below works when the value of :names being passed in is CML without any quotes.
OracleParameter param = new OracleParameter(":names", OracleDbType.Varchar2, "CML", ParameterDirection.Input);
Below doesn't work when the value of :names being passed in is CML','ABC with quotes on the inside.
OracleParameter param = new OracleParameter(":names", OracleDbType.Varchar2, "CML','ABC", ParameterDirection.Input);
Why is that?
Does Oracle add in single quotes around the parameter when it's passed into the sql statement? Why doesn't it add quotes around the second case?
ODP.NET parameters do not work with multiple, comma separated values. Each parameter is treated as a single value, whatever kind of quotes it contains.
Oracle does not add quotes around parameter values when passed to a query. Quotes are just a way to write a VARCHAR value in a query, but when using parameters, Oracle doesn't "replace your parameter with its value then execute the query", as this would allow SQL injection.
If that was the case, imagine your parameter value was: "CML', 'ABC');DROP DATABASE Test;--". Oracle would then execute SELECT name FROM Pers p WHERE p.FirstName IN ('CML', 'ABC');DROP DATABASE Test;--'!
See this question for ideas on how to solve your problem: Oracle Parameters with IN statement?
From your comments/answers I was able to come up with this solution. I hope it helps others who come.
To get around ODT.NET parameters not working with multiple comma separated values you can divide each value into its own parameter. Like the following.
string allParams = "CML, ABC, DEF";
string formattedParams = allParams.Replace(" ", string.Empty); // Or a custom format
string [] splitParams = formattedParams.Split(',');
List<OracleParamter> parameters = new List<OracleParameter>();
string sql = #"SELECT * FROM FooTable WHERE FooValue IN (";
for(int i = 0; i < splitParams.Length; i++)
{
sql += #":FooParam" + i + ",";
parameters.Add(new OracleParameter(":FooParam" + i, OracleDbType.Varchar2, splitParams[i], ParameterDirection.Input));
{
sql = sql.Substring(0, (sql.Length - 1));
sql += ')';
The string sql will now have this as it's value: SELECT * FROM FooTable WHERE FooValue IN (:FooParam0,:fooParam1, etc...)
This will solve the problem.
Another approach would be to add in a bunch of OR clauses for each parameter. The above example is better since you don't write a bunch of OR clauses though.

Execute stored procedure with array-like string separated by comma [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Help with a sql search query using a comma delimitted parameter
I want to write a stored procedure that performs a select on a table and need one input variable of type varchar(max).
I'd like to send a bunch of values separated by , as the input parameter, e.g.
'Jack','Jane','Joe'
and then get the rows that contain one of these names.
In SQL the code would be
Select * from Personnel where Name in ('Jack','Joe','Jane');
Now I want to have a variable in my C# app, say strNames and fill it like
string strNames = "'Jack','Joe','Jane'";
and send this variable to the SP and execute it. Something like
Select * from Personnel where Name in (''Jack','Joe','Jane'') -- this is wrong
But how can I tell SQL Server to run such command?
I need to make this happen and I know it's possible, please give me the clue.
First of all, the single names don't need to be quoted when you pass them to the stored procedure.
using (SqlCommand cmd = new SqlCommand("MyStoredProc", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#longFilter", "Jack,Jill,Joe");
using (SqlDataReader reader = cmd.ExecuteReader())
{
...
}
}
Then, in the stored procedure, you can use simple text functions and a temporary table as follows to split up the string at the commas and an an entry to the temporary table for each part of the string:
DECLARE #temp AS TABLE (Name NVARCHAR(255))
IF ISNULL(#longFilter, '') <> ''
BEGIN
DECLARE #s NVARCHAR(max)
WHILE LEN(#longFilter) > 0
BEGIN
IF CHARINDEX(',', #longFilter) > 0
BEGIN
SET #s = LTRIM(RTRIM(SUBSTRING(#longFilter, 1, CHARINDEX(',', #longFilter) - 1)))
SET #longFilter = SUBSTRING(#longFilter, CHARINDEX(',', #longFilter) + 1, LEN(#longFilter))
END ELSE
BEGIN
SET #s = LTRIM(RTRIM(#longFilter))
SET #longFilter= ''
END
-- This was missing until 20140522
INSERT INTO #temp (Name) VALUES (#s)
END
END
Later use the following SELECT to get a list of all people the name of which is in #temp or all of them if #temp doesn't contain any rows (unfiltered result):
SELECT * FROM Personnel WHERE Name IN (SELECT Name FROM #temp) OR (SELECT COUNT(*) FROM #temp) = 0
You could use Table Valued Parameters.
Basically, you could insert a list of values as a parameter in the procedure, and use them as a table, something along the lines of
Select * from Personnel
where Name in (select name from #NamesTable).
Now, the specifics
To use table valued parameters, the type of the parameter must be predefined in sql server, using
create type NamesTable as table (Name varchar(50))
You can then use the defined type as a parameter in the procedure
create procedure getPersonnelList
#NamesTable NamesTable readonly
as
begin
select * from personnel
where Name in (select Name from #NamesTable)
end
You can see that in action, in this SQL Fiddle
On the C# side of things you need to create the parameter. If you have the names in a collection, and build the string, you can just use that to generate the parameter, and if they are a comma-separated string, a quick string.Split could take care of that. Since I do not know your specifics, I'll assume you have a List<string> called names. You'll need to convert that to a table valued parameter to be sent to the procedure, using something like:
DataTable tvparameter = new DataTable();
tvparameter.Columns.Add("Name", typeof(string));
foreach (string name in names)
{
tvparameter.Rows.Add(name);
}
You can find more info on how to generate a TVP in C# code in the SO Question..
Now you just need to send that parameter to the procedure, and that's that. Here is a complete console program that executes the procedure and outputs the results.
List<string> names = new List<string> { "Joe", "Jane", "Jack" };
using (SqlConnection cnn = new SqlConnection("..."))
{
cnn.Open();
using (SqlCommand cmd = new SqlCommand("getPersonnelList", cnn))
{
cmd.CommandType = CommandType.StoredProcedure;
DataTable tvparameter = new DataTable();
tvparameter.Columns.Add("Name", typeof(string));
foreach (string name in names)
{
tvparameter.Rows.Add(name);
}
cmd.Parameters.AddWithValue("#NamesTable", tvparameter);
using (SqlDataReader dr = cmd.ExecuteReader())
{
while (dr.Read())
{
Console.WriteLine("{0} - {1}", dr["ID"], dr["Name"]);
}
}
}
}
I guess you need Split Function in Sql Server to break Comma-Separated Strings into Table. Please refer these links.
Split Function in Sql Server to break Comma-Separated Strings into Table
SQL User Defined Function to Parse a Delimited String
You can select the data from table using
Select * from
Personnel where
Name in (select items from dbo.Split ('Jack,Joe,Jane',','))
You could simply check if Name is contained in the string. Note the commas at the start of the end to ensure you match the full name
string strNames = ",Jack,Joe,Jane,";
The the SQL becomes
select * from Personnel where PATINDEX('%,' + Name + ',%', #strNames) > 0
See http://www.sqlfiddle.com/#!3/8ee5a/1

Oracle Parameters with IN statement?

Got a c#.net app which I need to modify. The query at the moment effectively does this:
select * from contract where contractnum = :ContractNum
(very simplified, just to show we're using an = and one parameter)
That parameter is read in from the Settings.Settings file on the C# app and has one string in it. I need to modify it to include multiple contracts, so I figure I can change the SQL to:
select * from contract where contractnum in (:ContractNum)
but that returns no results, no matter how I format the string in the parameter.
Is there a way I can get oracle to do an IN with a parameter?
You can use an Oracle collection of numbers as a parameter (bind variable) when you use ODP.NET as dataprovider. This works with Oracle server 9, 10 or 11 and ODP.net release >= 11.1.0.6.20 .
A similar solution is possible when you use Devart's .NET dataprovider for Oracle.
Let's select the contracts with contractnum's 3 and 4.
We have to use an Oracle type to transfer an array of contract numbers to our query.
MDSYS.SDO_ELEM_INFO_ARRAY is used because if we use this already predefined Oracle type we don't have to define our own Oracle type. You can fill MDSYS.SDO_ELEM_INFO_ARRAY with max 1048576 numbers.
using Oracle.DataAccess.Client;
using Oracle.DataAccess.Types;
[OracleCustomTypeMappingAttribute("MDSYS.SDO_ELEM_INFO_ARRAY")]
public class NumberArrayFactory : IOracleArrayTypeFactory
{
public Array CreateArray(int numElems)
{
return new Decimal[numElems];
}
public Array CreateStatusArray(int numElems)
{
return null;
}
}
private void Test()
{
OracleConnectionStringBuilder b = new OracleConnectionStringBuilder();
b.UserID = "sna";
b.Password = "sna";
b.DataSource = "ora11";
using (OracleConnection conn = new OracleConnection(b.ToString()))
{
conn.Open();
using (OracleCommand comm = conn.CreateCommand())
{
comm.CommandText =
#" select /*+ cardinality(tab 10) */ c.* " +
#" from contract c, table(:1) tab " +
#" where c.contractnum = tab.column_value";
OracleParameter p = new OracleParameter();
p.OracleDbType = OracleDbType.Array;
p.Direction = ParameterDirection.Input;
p.UdtTypeName = "MDSYS.SDO_ELEM_INFO_ARRAY";
//select contract 3 and 4
p.Value = new Decimal[] { 3, 4 };
comm.Parameters.Add(p);
int numContracts = 0;
using (OracleDataReader reader = comm.ExecuteReader())
{
while (reader.Read())
{
numContracts++;
}
}
conn.Close();
}
}
}
The index on contract.contractnum isn't used when one omits hint /*+ cardinality(tab 10) */. I assumed contractnum is the primary key so this column will be indexed.
See also here: http://forums.oracle.com/forums/thread.jspa?messageID=3869879#3869879
you could use a pipelined function to transform a string into a table which could be used with the IN operator. For example (tested with 10gR2):
SQL> select * from table(demo_pkg.string_to_tab('i,j,k'));
COLUMN_VALUE
-----------------
i
j
k
with the following package:
SQL> CREATE OR REPLACE PACKAGE demo_pkg IS
2 TYPE varchar_tab IS TABLE OF VARCHAR2(4000);
3 FUNCTION string_to_tab(p_string VARCHAR2,
4 p_delimiter VARCHAR2 DEFAULT ',')
5 RETURN varchar_tab PIPELINED;
6 END demo_pkg;
7 /
Package created
SQL> CREATE OR REPLACE PACKAGE BODY demo_pkg IS
2 FUNCTION string_to_tab(p_string VARCHAR2,
3 p_delimiter VARCHAR2 DEFAULT ',')
4 RETURN varchar_tab PIPELINED IS
5 l_string VARCHAR2(4000) := p_string;
6 l_first_delimiter NUMBER := instr(p_string, p_delimiter);
7 BEGIN
8 LOOP
9 IF nvl(l_first_delimiter,0) = 0 THEN
10 PIPE ROW(l_string);
11 RETURN;
12 END IF;
13 PIPE ROW(substr(l_string, 1, l_first_delimiter - 1));
14 l_string := substr(l_string, l_first_delimiter + 1);
15 l_first_delimiter := instr(l_string, p_delimiter);
16 END LOOP;
17 END;
18 END demo_pkg;
19 /
Package body created
Your query would look like this:
select *
from contract
where contractnum in (select column_value
from table(demo_pkg.string_to_tab(:ContractNum)))
Have yet to find a db that supports evaluating a single string variable containing commas to separate as the sole IN clause.
Your options are to substring the variable so the comma delimited variable contents are turned into rows, so you can then join onto this. Or to use dynamic SQL, which is a SQL statement constructed as a string in a sproc before the statement is executed.
I know this is an old question but it is one of several in which the selected answer did not solve my problem and I don't want to start yet another thread on this topic so I'll just put down what I found in my travels in the hope that it might help someone.
I don't work with Oracle much but, like in SQL Server, it seems that to pass a table-valued parameter you need to have a corresponding UDT (user defined table) to which you have EXECUTE permissions (I could be wrong). This means that other answers suggesting the use of a built-in SYS UDT come with some freight and I couldn't figure out whether it really is possible to pass a table to something that is not a PL/SQL stored procedure in the current version of ODP.net.
Second, the string-parse solution is a kludge for all the obvious reasons (can't cache the execution plan or whatever Oracle calls it, doesn't scale well, etc).
So I spent rather a lot of time trying do the IN-clause using a table-valued parameter on a datamart to which I have only READ permission before I was hit by a blinding flash of the obvious (At an ASP.net forum no less). Turns out Oracle supports Xml queries 'natively' so instead of passing an array of values you can pass an xml list (if that is all you need). Again, I may be wrong, but it gets handled as a legitimate bind parameter and this is an example of how simple it is to use (vb.net, ADO.net, ODP.net using NuGet package):
Dim xe As New XElement("l", New XElement("i", "ITEM-A"), New XElement("i", "ITEM-B"))
Using conn As New OracleConnection(myConnectionString)
conn.Open()
Using cmd As OracleCommand = conn.CreateCommand()
cmd.CommandType = CommandType.Text
Dim query As String
query = " SELECT s.FOO, q.BAR " & vbCrLf
query &= " FROM TABLE1 s LEFT OUTER JOIN " & vbCrLf
query &= " TABLE2 q ON q.ID = s.ID " & vbCrLf
query &= " WHERE (COALESCE(q.ID, 'NULL') NOT LIKE '%OPTIONAL%') AND "
query &= " (s.ID IN ("
query &= " SELECT stid "
query &= " FROM XMLTable('/l/i' PASSING XMLTYPE(:stid) COLUMNS stid VARCHAR(32) PATH '.')"
query &= " )"
query &= " )"
cmd.CommandText = query
Dim parameter As OracleParameter = cmd.Parameters.Add("stid", OracleDbType.NVarchar2, 4000)
parameter.Value = xe.ToString
Using r As OracleDataReader = cmd.ExecuteReader
While r.Read()
//Do something
End While
End Using
End Using
conn.Close()
This is more of an observation than a carefully researched solution so please comment if there is anything inappropriate about doing it this way.
There is apparently a 4000 character limit using this method (2000 if NVARCHAR) so I had to watch my paging. The informative error message you get if you go over is
ORA-01460: unimplemented or unreasonable conversion requested
For using parameter with IN statement you can use this construction:
select * from contract where contractnum
in (select column_value from table (:ContractNum))
where ContractNum is the custom array type.
Maybe someone is still looking for an answer, here's an example with rexexp.
https://blogs.oracle.com/aramamoo/passing-comma-separated-string-as-bind-variable-for-vo-querys-in-operator-v2
In this case, each emp no is coma separated
WHERE Emp.ENAME in
(select regexp_substr(:Bind_Ename_Comma_Sep_List,'[^,]+', 1, level)
from dual
connect by
regexp_substr(:Bind_Ename_Comma_Sep_List, '[^,]+', 1, level)
is not null)
Another way is to use the INSTR function: For example if the comma delimited data is in :xyz then you can determine if a value in a named item fldOne is in that list as follows:
INSTR(',' || :xyz || ',', ',' || fldOne ||',') > 0
will return true if the value fldOne references is in the list and false otherwise.
Of course, if you want to match against an explicit string, then you can replace fldOne with 'value'.
BTW this will also work if you created a comma separated variable using ListAgg or any other mechanism.
If the comma separated list was named MyList then just replace :xyz above with MyList.

String list in SqlCommand through Parameters in C#

Working with a SqlCommand in C# I've created a query that contains a IN (list...) part in the where clause. Instead of looping through my string list generating the list I need for the query (dangerous if you think in sqlInjection). I thought that I could create a parameter like:
SELECT blahblahblah WHERE blahblahblah IN #LISTOFWORDS
Then in the code I try to add a parameter like this:
DataTable dt = new DataTable();
dt.Columns.Add("word", typeof(string));
foreach (String word in listOfWords)
{
dt.Rows.Add(word);
}
comm.Parameters.Add("LISTOFWORDS", System.Data.SqlDbType.Structured).Value = dt;
But this doesn't work.
Questions:
Am I trying something impossible?
Did I took the wrong approach?
Do I have mistakes in this approach?
Thanks for your time :)
What you are trying to do is possible but not using your current approach. This is a very common problem with all possible solutions prior to SQL Server 2008 having trade offs related to performance, security and memory usage.
This link shows some approaches for SQL Server 2000/2005
SQL Server 2008 supports passing a table value parameter.
I hope this helps.
You want to think about where that list comes from. Generally that information is in the database somewhere. For example, instead of this:
SELECT * FROM [Table] WHERE ID IN (1,2,3)
You could use a subquery like this:
SELECT * FROM [Table] WHERE ID IN ( SELECT TableID FROM [OtherTable] WHERE OtherTableID= #OtherTableID )
If I understand right, you're trying to pass a list as a SQL parameter.
Some folks have attempted this before with limited success:
Passing Arrays to Stored Procedures
Arrays and Lists in SQL 2005
Passing Array of Values to SQL Server without String Manipulation
Using MS SQL 2005's XML capabilities to pass a list of values to a command
Am I trying something impossible?
No, it isn't impossible.
Did I took the wrong approach?
Your approach is not working (at least in .net 2)
Do I have mistakes in this approach?
I would try "Joel Coehoorn" solution (2nd answers) if it is possible.
Otherwise, another option is to send a "string" parameter with all values delimited by an separator. Write a dynamic query (build it based on values from string) and execute it using "exec".
Another solution will be o build the query directly from code. Somthing like this:
StringBuilder sb = new StringBuilder();
for (int i=0; i< listOfWords.Count; i++)
{
sb.AppendFormat("p{0},",i);
comm.Parameters.AddWithValue("p"+i.ToString(), listOfWords[i]);
}
comm.CommandText = string.Format(""SELECT blahblahblah WHERE blahblahblah IN ({0})",
sb.ToString().TrimEnd(','));
The command should look like:
SELECT blah WHERE blah IN (p0,p1,p2,p3...)...p0='aaa',p1='bbb'
In MsSql2005, "IN" is working only with 256 values.
I would recommend setting the parameter as a comma delimited string of values and use a Split function in SQL to turn that into a single column table of values and then you can use the IN feature.
http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=50648 - Split Functions
If you want to pass the list as a string in a parameter, you could just build the query dynamically.
DECLARE #query varchar(500)
SET #query = 'SELECT blah blah WHERE blahblah in (' + #list + ')'
EXECUTE(#query)
I used to have the same problem, I think there is now way to do this directly over the ADO.NET API.
You might consider inserting the words into a temptable (plus a queryid or something) and then refering to that temptable from the query. Or dynamically creating the query string and avoid sql injection by other measures (e.g. regex checks).
This is an old question but I've come up with an elegant solution for this that I love to reuse and I think everyone else will find it useful.
First of all you need to create a FUNCTION in SqlServer that takes a delimited input and returns a table with the items split into records.
Here is the following code for this:
ALTER FUNCTION [dbo].[Split]
(
#RowData nvarchar(max),
#SplitOn nvarchar(5) = ','
)
RETURNS #RtnValue table
(
Id int identity(1,1),
Data nvarchar(100)
)
AS
BEGIN
Declare #Cnt int
Set #Cnt = 1
While (Charindex(#SplitOn,#RowData)>0)
Begin
Insert Into #RtnValue (data)
Select
Data = ltrim(rtrim(Substring(#RowData,1,Charindex(#SplitOn,#RowData)-1)))
Set #RowData = Substring(#RowData,Charindex(#SplitOn,#RowData)+1,len(#RowData))
Set #Cnt = #Cnt + 1
End
Insert Into #RtnValue (data)
Select Data = ltrim(rtrim(#RowData))
Return
END
You can now do something like this:
Select Id, Data from dbo.Split('123,234,345,456',',')
And fear not, this can't be susceptible to Sql injection attacks.
Next write a stored procedure that takes your comma delimited data and then you can write a sql statement that uses this Split function:
CREATE PROCEDURE [dbo].[findDuplicates]
#ids nvarchar(max)
as
begin
select ID
from SomeTable with (nolock)
where ID in (select Data from dbo.Split(#ids,','))
end
Now you can write a C# wrapper around it:
public void SomeFunction(List<int> ids)
{
var idsAsDelimitedString = string.Join(",", ids.Select(id => id.ToString()).ToArray());
// ... or however you make your connection
var con = GetConnection();
try
{
con.Open();
var cmd = new SqlCommand("findDuplicates", con);
cmd.Parameters.Add(new SqlParameter("#ids", idsAsDelimitedString));
var reader = cmd.ExecuteReader();
// .... do something here.
}
catch (Exception)
{
// catch an exception?
}
finally
{
con.Close();
}
}

Categories