My C# side is like :
if(Request.QueryString["ValuesFromUser"]!=null)
{
ValuesFromUser_ = Request["ValuesFromUser"];
}
DataTable dtle = new DataTable();
SqlDataAdapter sqda;
sqda = new SqlDataAdapter("Checkforuserinput", Connection);
SqlParameter SQP = sqda.SelectCommand.Parameters.Add("#arg", SqlDbType.VarChar);
SQP.Direction = ParameterDirection.Input;
SQP.Value = "ValuesFromUser_";
sqda.Fill(dtle );
User will pass few arguments like "user1,user2,user3"
In my sql side :
Create PROC [dbo].[Checkforuserinput] #arg VARCHAR(50)= 'All'
As
Select *
from UserData
where User in (SELECT *
FROM SplitDelimiterString(#Arg, ','))
And SplitDelimiterString Function is like :
ALTER FUNCTION [dbo].[SplitDelimiterString] (#StringWithDelimiter VARCHAR(8000), #Delimiter VARCHAR(8))
RETURNS #ItemTable TABLE (Item VARCHAR(8000))
AS
BEGIN
DECLARE #StartingPosition INT;
DECLARE #ItemInString VARCHAR(8000);
SELECT #StartingPosition = 1;
--Return if string is null or empty
IF LEN(#StringWithDelimiter) = 0 OR #StringWithDelimiter IS NULL RETURN;
WHILE #StartingPosition > 0
BEGIN
--Get starting index of delimiter .. If string
--doesn't contain any delimiter than it will returl 0
SET #StartingPosition = CHARINDEX(#Delimiter,#StringWithDelimiter);
--Get item from string
IF #StartingPosition > 0
SET #ItemInString = SUBSTRING(#StringWithDelimiter,0,#StartingPosition)
ELSE
SET #ItemInString = #StringWithDelimiter;
--If item isn't empty than add to return table
IF( LEN(#ItemInString) > 0)
INSERT INTO #ItemTable(Item) VALUES (#ItemInString);
--Remove inserted item from string
SET #StringWithDelimiter = SUBSTRING(#StringWithDelimiter,#StartingPosition +
LEN(#Delimiter),LEN(#StringWithDelimiter) - #StartingPosition)
--Break loop if string is empty
IF LEN(#StringWithDelimiter) = 0 BREAK;
END
RETURN
END
Is this code safe or in risk for SQL injection?
Or should i use this :
Create PROC [dbo].[Checkforuserinput] #arg VARCHAR(50)= 'All'
As
declare #query nvarchar(max)
set #query = 'Select * from UserData where User in ('+#Arg+')'
EXECUTE sp_executesql #query
I don't see any forseeable problems with the split function, but passing a table valued parameter would be more efficient.
SQL
CREATE TYPE ArrayOfString AS TABLE
(
Item VARCHAR(50)
);
GO
CREATE PROC [dbo].[Checkforuserinput] #arg ArrayOfString READONLY
AS
SELECT *
FROM UserData
WHERE User IN (SELECT Item FROM #arg)
GO
C#
Write a helper function to write any IEnumerable(string) into a DataTable that the SQL Server will understand.
public static class SqlExtensions
{
public static DataTable ToSqlArray(this IEnumerable<string> collection)
{
var dt = new DataTable("ArrayOfString");
dt.Columns.Add(new DataColumn("Item", typeof(string)));
foreach(var item in collection)
{
var row = dt.NewRow();
row[0] = item;
dt.Rows.Add(row);
}
return dt;
}
}
Usage:
if(Request.QueryString["ValuesFromUser"]!=null)
{
ValuesFromUser_ = Request["ValuesFromUser"];
}
var values = ValuesFromUser_.Split(",");
using(var adapter = new SqlDataAdapter("Checkforuserinput", Connection))
{
adapter.SelectCommand.CommandType = CommandType.StoredProcedure;
adapter.SelectCommand.Parameters.AddWithValue("#arg", values.ToSqlArray());
adapter.Fill(dtle);
}
Depending on your version of SQL Server, you may want to consider passing a table-valued parameter to your stored procedure.
Table-valued parameters provide an easy way to marshal multiple rows of data from a client application to SQL Server without requiring multiple round trips or special server-side logic for processing the data. You can use table-valued parameters to encapsulate rows of data in a client application and send the data to the server in a single parameterized command. The SqlParameter is populated by using the AddWithValue method and the SqlDbType is set to Structured.
The following link provides a good overview:
http://msdn.microsoft.com/en-us/library/bb675163(v=vs.110).aspx
Related
I'm trying to call the below stored procedure but I am unsure on what to pass through one of the parameters (#UnsubscribeTypes) I've tried passing in a list but got a compile error. I'm using c#, Visual Studio 2010, web forms. Any ideas on what I should pass in when calling the stored procedure in my c# code (ado.net)?
Here is my stored procedure:
ALTER PROCEDURE [czone].[SetAccountEmailPreference]
(
#EmailAddress VARCHAR(255),
#UnsubscribeTypes dbo.ListOfIDs READONLY,
#SentEmailID INT = NULL
)
AS
SET NOCOUNT ON;
EXEC dbo.LogObjectExecution ##PROCID;
DECLARE #UnsubscribeID INT = (SELECT TOP 1 UnsubscribeID
FROM Email.dbo.Unsubscribe
WHERE EmailAddress = #EmailAddress
ORDER BY UnsubscribeID DESC);
-- Unsubscribe
IF ((SELECT COUNT(*) FROM #UnsubscribeTypes) > 0)
BEGIN
IF(#UnsubscribeID IS NULL)
BEGIN
-- ADD UNSUBSCRIBE
INSERT INTO Email.dbo.Unsubscribe (EmailAddress, CreatedDate)
VALUES (#EmailAddress, CURRENT_TIMESTAMP)
SET #UnsubscribeID = ##IDENTITY;
END
-- Remove current mappings
DELETE FROM Email.dbo.UnsubscribeTypeMapping
WHERE UnsubscribeFK = #UnsubscribeID;
-- Add new mappings
INSERT INTO Email.dbo.UnsubscribeTypeMapping (UnsubscribeFK, UnsubscribeTypeFK, SentEmailFK)
SELECT
#UnsubscribeID, ID, #SentEmailID
FROM
#UnsubscribeTypes;
END
-- Subscribe
ELSE IF (#UnsubscribeID IS NOT NULL)
BEGIN
DELETE FROM Email.dbo.Unsubscribe
WHERE UnsubscribeID = #UnsubscribeID;
END
dbo.ListOfIDs is a table type. First, find out the type in your database, then check columns. generate a datatable with rows containing the UnsubscribeTypeFK ids.
The ADO.net code (not compiled)
Creating table
DataTable dt = new DataTable("Items");
dt.Columns.Add("ID", typeof(int));
dt.Rows.Add(4);
Calling proc
con = new SqlConnection(conStr);
con.Open();
using (con) {
// Configure the SqlCommand and SqlParameter.
SqlCommand sqlCmd = new SqlCommand("czone.SetAccountEmailPreference", con);
sqlCmd.CommandType = CommandType.StoredProcedure;
SqlParameter tvpParam = sqlCmd.Parameters.AddWithValue("#UnsubscribeTypes", _dt); // TVP
tvpParam.SqlDbType = SqlDbType.Structured; //tells ADO.NET we are passing TVP
//pass other parameters
sqlCmd.ExecuteNonQuery();
}
con.Close();
You will find more about Table-Valued parameters here
I am calling a SQL Server stored procedure from my C# code:
using (SqlConnection conn = new SqlConnection(connstring))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand("InsertQuerySPROC", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
var STableParameter = cmd.Parameters.AddWithValue("#QueryTable", QueryTable);
var NDistanceParameter = cmd.Parameters.AddWithValue("#NDistanceThreshold", NDistanceThreshold);
var RDistanceParameter = cmd.Parameters.AddWithValue(#"RDistanceThreshold", RDistanceThreshold);
STableParameter .SqlDbType = SqlDbType.Structured;
NDistanceParameter.SqlDbType = SqlDbType.Int;
RDistanceParameter.SqlDbType = SqlDbType.Int;
// Execute the query
SqlDataReader QueryReader = cmd.ExecuteReader();
My stored proc is fairly standard but does a join with QueryTable (hence the need for for using a stored proc).
Now: I want to add a list of strings, List<string>, to the parameter set. For example, my stored proc query goes like this:
SELECT feature
FROM table1 t1
INNER JOIN #QueryTable t2 ON t1.fid = t2.fid
WHERE title IN <LIST_OF_STRINGS_GOES_HERE>
However, the list of strings is dynamic and a few hundred long.
Is there a way to pass a list of strings List<string> to the stored proc??? Or is there a better way to do this?
Many thanks,
Brett
If you're using SQL Server 2008, there's a new featured called a User Defined Table Type. Here is an example of how to use it:
Create your User Defined Table Type:
CREATE TYPE [dbo].[StringList] AS TABLE(
[Item] [NVARCHAR](MAX) NULL
);
Next you need to use it properly in your stored procedure:
CREATE PROCEDURE [dbo].[sp_UseStringList]
#list StringList READONLY
AS
BEGIN
-- Just return the items we passed in
SELECT l.Item FROM #list l;
END
Finally here's some sql to use it in c#:
using (var con = new SqlConnection(connstring))
{
con.Open();
using (SqlCommand cmd = new SqlCommand("exec sp_UseStringList #list", con))
{
using (var table = new DataTable()) {
table.Columns.Add("Item", typeof(string));
for (int i = 0; i < 10; i++)
table.Rows.Add("Item " + i.ToString());
var pList = new SqlParameter("#list", SqlDbType.Structured);
pList.TypeName = "dbo.StringList";
pList.Value = table;
cmd.Parameters.Add(pList);
using (var dr = cmd.ExecuteReader())
{
while (dr.Read())
Console.WriteLine(dr["Item"].ToString());
}
}
}
}
To execute this from SSMS
DECLARE #list AS StringList
INSERT INTO #list VALUES ('Apple')
INSERT INTO #list VALUES ('Banana')
INSERT INTO #list VALUES ('Orange')
-- Alternatively, you can populate #list with an INSERT-SELECT
INSERT INTO #list
SELECT Name FROM Fruits
EXEC sp_UseStringList #list
The typical pattern in this situation is to pass the elements in a comma delimited list, and then in SQL split that out into a table you can use. Most people usually create a specified function for doing this like:
INSERT INTO <SomeTempTable>
SELECT item FROM dbo.SplitCommaString(#myParameter)
And then you can use it in other queries.
No, arrays/lists can't be passed to SQL Server directly.
The following options are available:
Passing a comma-delimited list and then having a function in SQL split the list. The comma delimited list will most likely be passed as an Nvarchar()
Pass xml and have a function in SQL Server parse the XML for each value in the list
Use the new defined User Defined table type (SQL 2008)
Dynamically build the SQL and pass in the raw list as "1,2,3,4" and build the SQL statement. This is prone to SQL injection attacks, but it will work.
Yep, make Stored proc parameter as VARCHAR(...)
And then pass comma separated values to a stored procedure.
If you are using Sql Server 2008 you can leverage TVP (Table Value Parameters): SQL 2008 TVP and LINQ if structure of QueryTable more complex than array of strings otherwise it would be an overkill because requires table type to be created within SQl Server
Make a datatable with one column instead of List and add strings to the table. You can pass this datatable as structured type and perform another join with title field of your table.
If you prefer splitting a CSV list in SQL, there's a different way to do it using Common Table Expressions (CTEs). See Efficient way to string split using CTE.
The only way I'm aware of is building CSV list and then passing it as string. Then, on SP side, just split it and do whatever you need.
CREATE TYPE [dbo].[StringList1] AS TABLE(
[Item] [NVARCHAR](MAX) NULL,
[counts][nvarchar](20) NULL);
create a TYPE as table and name it as"StringList1"
create PROCEDURE [dbo].[sp_UseStringList1]
#list StringList1 READONLY
AS
BEGIN
-- Just return the items we passed in
SELECT l.item,l.counts FROM #list l;
SELECT l.item,l.counts into tempTable FROM #list l;
End
The create a procedure as above and name it as "UserStringList1"
s
String strConnection = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString.ToString();
SqlConnection con = new SqlConnection(strConnection);
con.Open();
var table = new DataTable();
table.Columns.Add("Item", typeof(string));
table.Columns.Add("count", typeof(string));
for (int i = 0; i < 10; i++)
{
table.Rows.Add(i.ToString(), (i+i).ToString());
}
SqlCommand cmd = new SqlCommand("exec sp_UseStringList1 #list", con);
var pList = new SqlParameter("#list", SqlDbType.Structured);
pList.TypeName = "dbo.StringList1";
pList.Value = table;
cmd.Parameters.Add(pList);
string result = string.Empty;
string counts = string.Empty;
var dr = cmd.ExecuteReader();
while (dr.Read())
{
result += dr["Item"].ToString();
counts += dr["counts"].ToString();
}
in the c#,Try this
I have a stored procedure that returns 2 output parameters and a record.
But in C#:
ExecuteReader won't allow a returned value but allows records
ExecuteNonQuery allows a returned value but not record.
How can I get both?
The output parameter returned by a stored procedure are not available until you close the DataReader.
Supposing you have
SqlDataReader reader = cmd.ExecuteReader();
...... do you record reading
reader.Close();
// Now the output parameters are available
int result = (int)cmd.Parameters["OutputParameter1"].Value;
Of course this is supposing that you have correctly setup your output parameters....
This is from SqlDataReader docs on MSDN
While the SqlDataReader is being used, the associated SqlConnection is
busy serving the SqlDataReader, and no other operations can be
performed on the SqlConnection other than closing it. This is the case
until the Close method of the SqlDataReader is called. For example,
you cannot retrieve output parameters until after you call Close.
All sql server stored procedure return an int return code. By default, it is 0 if no return statement is executed all, or a simple return with no value is executed. Otherwise, the return code is set to the value specified on the return statement (such as return #someIntegerValue. This return code is accessible in C# by adding a parameter to the command's parameter's collection with its Direction property set to ParameterDirection.ReturnValue.
You can also return a value by using an output parameter.
This code should do you. It shows how to access both an output parameter and the procedure's int return code. I'm pouring the results set(s) into a DataSet... but opening a SqlDataReader will work just the same, though you won't be able to access any output parameters or the stored procedure's return code until the DataReader has finished consuming the results and finalized command execution.
static DataSet execSomeStoredProc( string tableName, out int columns , out DateTime? dtCreated )
{
const string connectString = #"Server=localhost;Database=sandbox;Trusted_Connection=True;" ;
DataSet ds = new DataSet();
using ( SqlConnection session = new SqlConnection(connectString) )
using ( SqlCommand cmd = session.CreateCommand() )
using ( SqlDataAdapter adapter = new SqlDataAdapter(cmd) )
{
cmd.CommandText = "dbo.my_simple_stored_procedure" ;
cmd.CommandType = CommandType.StoredProcedure;
// a simple input parameter
cmd.Parameters.AddWithValue( "#tableName" , tableName ) ;
// an output-only parameter
SqlParameter outputParameter = new SqlParameter( "#creationDate" , SqlDbType.DateTime , 8 ) ;
outputParameter.ParameterName = "#creationDate" ;
outputParameter.Direction = ParameterDirection.Output;
outputParameter.IsNullable = true ;
cmd.Parameters.Add( outputParameter ) ;
// the integer return code that all stored procedure returns.
// The default value of 0 is return if no return statement is executed, or
// a return statement with no value is executed.
SqlParameter rc = new SqlParameter("#rc" ,SqlDbType.Int ) ;
rc.Direction = ParameterDirection.ReturnValue;
cmd.Parameters.Add( rc ) ;
session.Open();
adapter.Fill(ds) ;
session.Close() ;
columns = (int) rc.Value ;
dtCreated = outputParameter.Value == DBNull.Value ? (DateTime?)null : (DateTime)outputParameter.Value ;
}
return ds ;
}
The above code, for what it's worth, is executing this stored procedure:
create procedure dbo.my_simple_stored_procedure
#tableName varchar(255) ,
#creationDate datetime output
as
set ansi_nulls on
set concat_null_yields_null on
set nocount on
declare #create_date datetime
declare #rows int
select *
from INFORMATION_SCHEMA.COLUMNS c
where c.TABLE_SCHEMA = 'dbo'
and c.TABLE_NAME = #tableName
order by c.TABLE_CATALOG ,
c.TABLE_SCHEMA ,
c.TABLE_NAME ,
c.ORDINAL_POSITION
set #rows = ##rowcount
select #create_date = st.create_date
from sys.tables st
where st.object_id = object_id( 'dbo.'+#tableName )
set #creationDate = #create_date
return #rows
go
I have a method in my application which is passing list<listitem> into a stored procedure. I have created a table type data type to pass list<listitem>. Now I need to loop through in stored procedure to insert into another table. For that purpose I created row id column in the table type which is generated automatically.
Since the table type has 2 columns, it expects 2 parameters to pass from outside but I am generating it by identity column. Is there any way to avoid so that I don't pass value form outside?
public void test(List<string> listItem) {
var table = new DataTable();
table.Columns.Add("col1", typeof(string));
foreach (string col1 in listItem) { table.Rows.Add(col1); }
SqlCommand cmd1 = new SqlCommand("TableTypeUpdateAnswers", conn);
cmd1.CommandType = CommandType.StoredProcedure;
SqlParameter sqlParam = cmd1.Parameters.AddWithValue("#tvpUpdateAnswers",table);
sqlParam.SqlDbType = SqlDbType.Structured ;
sqlParam.TypeName = "dbo.AnswerTableType";
conn.Open();
try
{ }
catch (Exception e)
{ }
}
Here is the SQL to create table type :
CREATE TYPE [dbo].[AnswerTableType] AS TABLE(
RowID int not null primary key identity(1,1),
[col1] [nvarchar](50) NULL
)
And here is the stored procedure:
ALTER PROCEDURE [dbo].[TestTableType]
#TVPUPDATEANSWERS DBO.ANSWERTABLETYPE READONLY
AS
DECLARE
#CURRENTROW INT,
#VANSID int ,
#ROWSTOPROCESS INT
BEGIN
SET #CURRENTROW=0
SELECT L.col1 FROM #TVPUPDATEANSWERS L;
SET #ROWSTOPROCESS=##ROWCOUNT
WHILE #CURRENTROW<#ROWSTOPROCESS
BEGIN
SET #CURRENTROW=#CURRENTROW+1
(SELECT #VANSID = col1
FROM #TVPUPDATEANSWERS
WHERE ROWID=#CURRENTROW);
//do something with table type datatype
INSERT INTO DBO.table1(col3,col4)
VALUES(#VANSID ,#col4);
END
END
Seems like you may benefit from using this kind of approach - check it out and let me know... http://ilovedevelopment.blogspot.co.uk/2012/01/manage-multiple-updates-to-data-using-c.html
I am calling a SQL Server stored procedure from my C# code:
using (SqlConnection conn = new SqlConnection(connstring))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand("InsertQuerySPROC", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
var STableParameter = cmd.Parameters.AddWithValue("#QueryTable", QueryTable);
var NDistanceParameter = cmd.Parameters.AddWithValue("#NDistanceThreshold", NDistanceThreshold);
var RDistanceParameter = cmd.Parameters.AddWithValue(#"RDistanceThreshold", RDistanceThreshold);
STableParameter .SqlDbType = SqlDbType.Structured;
NDistanceParameter.SqlDbType = SqlDbType.Int;
RDistanceParameter.SqlDbType = SqlDbType.Int;
// Execute the query
SqlDataReader QueryReader = cmd.ExecuteReader();
My stored proc is fairly standard but does a join with QueryTable (hence the need for for using a stored proc).
Now: I want to add a list of strings, List<string>, to the parameter set. For example, my stored proc query goes like this:
SELECT feature
FROM table1 t1
INNER JOIN #QueryTable t2 ON t1.fid = t2.fid
WHERE title IN <LIST_OF_STRINGS_GOES_HERE>
However, the list of strings is dynamic and a few hundred long.
Is there a way to pass a list of strings List<string> to the stored proc??? Or is there a better way to do this?
Many thanks,
Brett
If you're using SQL Server 2008, there's a new featured called a User Defined Table Type. Here is an example of how to use it:
Create your User Defined Table Type:
CREATE TYPE [dbo].[StringList] AS TABLE(
[Item] [NVARCHAR](MAX) NULL
);
Next you need to use it properly in your stored procedure:
CREATE PROCEDURE [dbo].[sp_UseStringList]
#list StringList READONLY
AS
BEGIN
-- Just return the items we passed in
SELECT l.Item FROM #list l;
END
Finally here's some sql to use it in c#:
using (var con = new SqlConnection(connstring))
{
con.Open();
using (SqlCommand cmd = new SqlCommand("exec sp_UseStringList #list", con))
{
using (var table = new DataTable()) {
table.Columns.Add("Item", typeof(string));
for (int i = 0; i < 10; i++)
table.Rows.Add("Item " + i.ToString());
var pList = new SqlParameter("#list", SqlDbType.Structured);
pList.TypeName = "dbo.StringList";
pList.Value = table;
cmd.Parameters.Add(pList);
using (var dr = cmd.ExecuteReader())
{
while (dr.Read())
Console.WriteLine(dr["Item"].ToString());
}
}
}
}
To execute this from SSMS
DECLARE #list AS StringList
INSERT INTO #list VALUES ('Apple')
INSERT INTO #list VALUES ('Banana')
INSERT INTO #list VALUES ('Orange')
-- Alternatively, you can populate #list with an INSERT-SELECT
INSERT INTO #list
SELECT Name FROM Fruits
EXEC sp_UseStringList #list
The typical pattern in this situation is to pass the elements in a comma delimited list, and then in SQL split that out into a table you can use. Most people usually create a specified function for doing this like:
INSERT INTO <SomeTempTable>
SELECT item FROM dbo.SplitCommaString(#myParameter)
And then you can use it in other queries.
No, arrays/lists can't be passed to SQL Server directly.
The following options are available:
Passing a comma-delimited list and then having a function in SQL split the list. The comma delimited list will most likely be passed as an Nvarchar()
Pass xml and have a function in SQL Server parse the XML for each value in the list
Use the new defined User Defined table type (SQL 2008)
Dynamically build the SQL and pass in the raw list as "1,2,3,4" and build the SQL statement. This is prone to SQL injection attacks, but it will work.
Yep, make Stored proc parameter as VARCHAR(...)
And then pass comma separated values to a stored procedure.
If you are using Sql Server 2008 you can leverage TVP (Table Value Parameters): SQL 2008 TVP and LINQ if structure of QueryTable more complex than array of strings otherwise it would be an overkill because requires table type to be created within SQl Server
Make a datatable with one column instead of List and add strings to the table. You can pass this datatable as structured type and perform another join with title field of your table.
If you prefer splitting a CSV list in SQL, there's a different way to do it using Common Table Expressions (CTEs). See Efficient way to string split using CTE.
The only way I'm aware of is building CSV list and then passing it as string. Then, on SP side, just split it and do whatever you need.
CREATE TYPE [dbo].[StringList1] AS TABLE(
[Item] [NVARCHAR](MAX) NULL,
[counts][nvarchar](20) NULL);
create a TYPE as table and name it as"StringList1"
create PROCEDURE [dbo].[sp_UseStringList1]
#list StringList1 READONLY
AS
BEGIN
-- Just return the items we passed in
SELECT l.item,l.counts FROM #list l;
SELECT l.item,l.counts into tempTable FROM #list l;
End
The create a procedure as above and name it as "UserStringList1"
s
String strConnection = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString.ToString();
SqlConnection con = new SqlConnection(strConnection);
con.Open();
var table = new DataTable();
table.Columns.Add("Item", typeof(string));
table.Columns.Add("count", typeof(string));
for (int i = 0; i < 10; i++)
{
table.Rows.Add(i.ToString(), (i+i).ToString());
}
SqlCommand cmd = new SqlCommand("exec sp_UseStringList1 #list", con);
var pList = new SqlParameter("#list", SqlDbType.Structured);
pList.TypeName = "dbo.StringList1";
pList.Value = table;
cmd.Parameters.Add(pList);
string result = string.Empty;
string counts = string.Empty;
var dr = cmd.ExecuteReader();
while (dr.Read())
{
result += dr["Item"].ToString();
counts += dr["counts"].ToString();
}
in the c#,Try this