From front end(studio 2008) I am passing values to sql procedure as :
string a = "hello" + "098765" + "world" + "90.0909"
These are 4 different values that I've concatenated into a string a;
now i pass this string a to the sql procedure using c# sqlCommand object.
Now, how do I retrieve these 4 values in sql procedure as I've created the procedure as:
create procedure Proc_name (#concatenated_string varchar(100))
as
insert into table1 values(**how can i get those 4 values here**).
I used arrays but it didn't work.
If you want to pass an array into SQL Server to deal with "multirow" updates on one table, read this famous article(s).
If you want a generic stored proc to update any table, then don't as per other comments
The standard way to do this would be to use four parameters on the procedure:
create procedure Proc_name (#param1 varchar(100),
#param2 varchar(100),
#param3 varchar(100),
#param4 varchar(100))
as
insert into table1 values(#param1, #param2, #param3, #param4)
Then from your code (giving a c# example using ADO.NET)
using (SqlConnection connection = new SqlConnection(connectionString))
{
// Create the command and set its properties.
SqlCommand command = new SqlCommand();
SqlCommand command = new SqlCommand
("Proc_name", connection);
command.CommandType = CommandType.StoredProcedure;
// Add the input parameters and set the properties.
SqlParameter parameter1 = new SqlParameter();
parameter.ParameterName = "#Param1";
parameter.SqlDbType = SqlDbType.NVarChar;
parameter.Direction = ParameterDirection.Input;
parameter.Value = param1;
SqlParameter parameter2 = new SqlParameter();
parameter.ParameterName = "#Param2";
parameter.SqlDbType = SqlDbType.NVarChar;
parameter.Direction = ParameterDirection.Input;
parameter.Value = param2;
// Same for params 3 and 4...
// Add the parameter to the Parameters collection.
command.Parameters.Add(parameter1);
command.Parameters.Add(parameter2);
command.Parameters.Add(parameter3);
command.Parameters.Add(parameter4);
// Open the connection and execute the reader.
connection.Open();
SqlDataReader reader = command.ExecuteNonQuery();
reader.Close();
}
If you are using SQL Server 2005 then you might want to look at sending your data through to your stored procedure as an XML parameter. This link explains the process perfectly
Here's a sample section of how your code might look using .NET 3.5 and C#
// sample object
[Serializable]
internal class MyClass
{
internal string Property1 { get; set; }
internal string Property2 { get; set; }
internal int Property3 { get; set; }
internal string Property4 { get; set; }
}
// sample serialization
internal static string SerializeObject<T>(T objectGraph)
{
StringBuilder sb = new StringBuilder();
XmlWriterSettings writerSettings = new XmlWriterSettings();
writerSettings.OmitXmlDeclaration = true;
writerSettings.Indent = true;
using (XmlWriter xmlWriter = XmlWriter.Create(sb, writerSettings))
{
XmlSerializer xs = new XmlSerializer(typeof(T));
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add(String.Empty, String.Empty);
xs.Serialize(xmlWriter, objectGraph, ns);
}
return sb.ToString();
}
// sample stored procedure
Create PROCEDURE [dbo].[MyProc]
#myClassXML XML
AS
BEGIN
INSERT INTO [dbo].[MyTable]
(
P1,
P2,
P3,
P4
)
SELECT
Container.ContainerCol.value('Property1[1]', 'varchar(50)') AS P1,
Container.ContainerCol.value('Property2[1]', 'varchar(50)') AS P2,
Container.ContainerCol.value('Property3[1]', 'int') AS P3,
Container.ContainerCol.value('Property4[1]', 'varchar(50)') AS P4,
FROM #myClassXML.nodes('//MyClass') AS Container(ContainerCol)
END
I am assuming that you've read the advice of other answers here and are not creating a generic "Insert Anything" stored procedure as this is one of the worst things that you could do.
Note: This code was written in Notepad++ and thus hasn't been tested.
You could concatenate the 4 strings with a comma between and split it in the database back.
E.g.
declare #values as nvarchar(1000)
set #values = 'hello,098765,world,90.0909'
SELECT * FROM split(#values)
---------------- SPLIT FUNCTION --------------
CREATE FUNCTION [dbo].[split]
(
#csv nvarchar(max)
)
RETURNS
#entries TABLE
(
entry nvarchar(100)
)
AS
BEGIN
DECLARE #commaindex int
SELECT #commaindex = CHARINDEX(',', #csv)
IF #commaindex > 0
BEGIN
INSERT INTO #entries
-- insert left side
SELECT LTrim(RTrim(LEFT(#csv, #commaindex-1)))
-- pass right side recursively
UNION ALL
SELECT entry
FROM dbo.split(RIGHT(#csv, LEN(#csv) - #commaindex))
END
ELSE
INSERT INTO #entries
SELECT LTrim(RTrim(#csv))
RETURN
END
use several parameters instead of 1, e.g.:
CREATE PROCEDURE [dbo].[addUser]
#idRole int,
#userName varchar(255),
#password varchar(255)
AS
BEGIN
set nocount on
insert into userTbl ( idRole , userName , password )
VALUES ( #idRole , #userName , #password )
return scope_identity();
END
GO
If you really do just want to use one parameter, then maybe consider an XML parameter rather than a string.
public List<T> updateSiteDetails<T>(int SiteId, int CategoryId, string[] values)
{
int temp = values.Count();
int Counter = 0;
List<T> SiteDetails = null;
var parameterData = new string[temp];
var para = new string[temp];
foreach (string value in values)
{
Counter =Counter++;
parameterData[Counter] = "#,value"+Counter;
para[Counter] = string.Format(","+value);
}
//string ParameterDatas=string.Join(",",parameterData);
string parameterValue = string.Join(",",para);
using (SBDEntities db = new SBDEntities())
{
SiteDetails = db.Database.SqlQuery<T>("Sp_Update_Data #SiteId,#CategoryId" + string.Join(",", parameterData),string.Join(",",para)
//new Object[] { new SqlParameter("#SiteId", SiteId),
// new SqlParameter("#CategoryId",CategoryId)}
).ToList();
}
return SiteDetails;
}
in case you are using stored procedure with Entity framework
Related
I've got the following method that is supposed to delete the Holiday names from the database given from the List's that are passed in as arguments. The problem I am having is it isn't deleting anything from the database. Here is part of the method that I am having issues with:
private void RemoveGloOrderDays(List<SessionInfoList> sessionList, List<Holiday> selectedOrderHolidays, List<Holiday> selectedHolidays, List<string> orderDays, List<string> noOrderDays)
{
try
{
using (SqlCommand cmd = new SqlCommand())
using (SqlConnection connection = new SqlConnection(ConnectionString))
{
cmd.CommandTimeout = 600;
cmd.CommandText = "[dbo].[RemoveGlobalOrderDays]";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Connection = connection;
cmd.Parameters.Add("#SessionId", SqlDbType.Int);
cmd.Parameters.Add("#SelectedOrderHolidays", SqlDbType.NVarChar);
cmd.Parameters.Add("#SelectedHolidays", SqlDbType.NVarChar);
cmd.Parameters.Add("#OrderDays", SqlDbType.NVarChar);
cmd.Parameters.Add("#NoOrderDays", SqlDbType.NVarChar);
connection.Open();
foreach (SessionInfoList session in sessionList)
{
cmd.Parameters["#SessionId"].Value = session.SessionID;
cmd.Parameters["#SelectedOrderHolidays"].Value = DBNull.Value;
string joinedNames = string.Join(",", selectedOrderHolidays.Select(h => h.Name.Trim()));
if (!string.IsNullOrEmpty(joinedNames))
{
cmd.Parameters["#SelectedOrderHolidays"].Value = joinedNames;
}
cmd.Parameters["#SelectedHolidays"].Value = DBNull.Value;
joinedNames = string.Join(",", selectedHolidays.Select(h => h.Name.Trim()));
if (!string.IsNullOrEmpty(joinedNames))
{
cmd.Parameters["#SelectedHolidays"].Value = joinedNames;
}
Here is my stored procedure:
IF OBJECT_ID('[dbo].[RemoveGlobalOrderDays]') IS NOT NULL
DROP PROCEDURE [dbo].[RemoveGlobalOrderDays]
GO
CREATE PROCEDURE [dbo].[RemoveGlobalOrderDays]
#SessionId int,
#SelectedHolidays nvarchar(500),
#SelectedOrderHolidays nvarchar(500),
#OrderDays nvarchar(500),
#NoOrderDays nvarchar(500)
WITH ENCRYPTION
AS
BEGIN
SET NOCOUNT ON;
UPDATE [cfgSchedule]
SET
[OrderDays] = #OrderDays,
[NoOrderDays] = #NoOrderDays
WHERE [cfgSchedule].[SessionId] = #SessionID
DELETE FROM [SessionHolidayMapping]
WHERE [HolidayName] = #SelectedHolidays
AND
[SessionId] = #SessionId
DELETE FROM [SessionOrderHolidayMapping]
WHERE [SessionId] = #SessionId
AND
[HolidayName] = #SelectedOrderHolidays
END
GO
As far as I can see you are passing list of names separated by comma and you want to delete all those names. You need to use IN operator to find all holiday names that should be deleted.
Here is an example how to do it for #SelectedHolidays:
declare #SelectedHolidays nvarchar(500) = 'H1,H2,H3'
declare #SelectedHolidaysXml xml = cast(replace(N'<R><I>' + #SelectedHolidays + N'</I></R>', ',', '</I><I>') as xml)
DELETE FROM [SessionHolidayMapping]
WHERE [HolidayName] in (select x.items.value('(.)[1]', 'NVARCHAR(500)') from #SelectedHolidaysXml.nodes('/R/I') as x(items))
AND [SessionId] = #SessionId
It is ugly, but I don't know of better way to split comma separated values in sql server.
Instead of using nvarchar, you could use table valued parameters for the parameters #Selectedholidays and #selectedorderholidays and then something like
DELETE [SessionHolidayMapping] FROM [SessionHolidayMapping] A
Inner join #selectedholidays S
On A.[HolidayName] = S.Holidayname where A.[SessionId] = #SessionId.
The "HolidayName" is one column of the parameter.
I'm on my phone and cannot test it appropriately.
In SQL Server Management Studio:
Right click the procedure
Execute
Do not enter value for the OUTPUT parameter
Enter value for another string parameter
The correct value is returned (ex: 12+)
Calling code:
DECLARE #return_value int,
#CustomerID bigint
EXEC #return_value = [dbo].[InsertCustomer]
#CustomerID = #CustomerID OUTPUT,
#Name = N'CustomerName'
SELECT #CustomerID as N'#CustomerID'
SELECT 'Return Value' = #return_value
In application code:
Run the following code
The returned value for CustomerID is always 1
Code:
ObjectParameter ob = new ObjectParameter("CustomerID", typeof(long));
var CustomerID = db.InsertCustomer(ob, "CustomerName");
I attempted to change the new ObjectParameter(,) second parameter by passing a type and often object by value (ex: 0, 1 etc) but with no avail.
What could have went wrong here?
Update:
This is how the Entity Framework procedure code look like:
public virtual int InsertCustomer(ObjectParameter customerID, string name)
{
var nameParameter = name != null ?
new ObjectParameter("Name", name) :
new ObjectParameter("Name", typeof(string));
return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction("InsertCustomer", customerID, nameParameter);
}
Straight ADO:
Please refer to this link.. You need to specify your output parameter in the proc and you need to specify it in the calling code.
SqlParameter outParam = new SqlParameter("CustomerID", 0);
using (SqlConnection con = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["StringName"].ConnectionString))
{
using (SqlCommand cmd = new SqlCommand("InsertCustomer", con))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#Name", SqlDbType.VarChar).Value = CustomerName;
cmd.Parameters.Add(outParam.ParameterName, SqlDbType.BigInt).Direction = ParameterDirection.Output;
con.Open();
var CustomerID = Convert.ToInt64(cmd.Parameters["CustomerID"].Value);
}
}
Entity Framework:
Refer to this link
ObjectParameter ob = new ObjectParameter("CustomerID", 0);
// Wrong
var CustomerID = db.InsertCustomer(ob, "CustomerName");
// Right
db.InsertCustomer(ob, "CustomerName");
var CustomerID = (long)ob.Value;
I have written a stored procedure:
USE [database]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [database table]
#StoreNum INT,
#CustomerID INT,
#r VARCHAR(100) OUTPUT
AS
BEGIN
Select #r = Notes FROM [Database table]
WHERE CustomerID = #CustomerID
AND StoreNumber = CAST(#StoreNum AS NVARCHAR(10))
END
When I run this stored procedure in SQL server management studio, #r returns the string I am expecting.
However with the following C# code below I get the error: "Cannot convert from int to string".
SqlParameter param4 = new SqlParameter("#StoreNum", storeaudit.StoreNumber);
SqlParameter param5 = new SqlParameter("#CustomerID", "9");
SqlParameter param7 = new SqlParameter("#r", "");
param5.SqlDbType = System.Data.SqlDbType.Int;
param7.Direction = System.Data.ParameterDirection.Output;
var NoteMan = db.Database.ExecuteSqlCommand("sp_GetNotesData #StoreNum, #CustomerID, #r", param4, param5, param7);
String managers = param7.Value.ToString();
System.Console.WriteLine(param7.Value);
Any help would be greatly appreciated, thanks
UPDATE - Added the output and sqltype to the parameter and now the string managers returns "".
you must change 2 things:
param5 value must be int
Set direction to output for param7
SqlParameter param4 = new SqlParameter("#StoreNum", storeaudit.StoreNumber);
SqlParameter param5 = new SqlParameter("#CustomerID", 9);
SqlParameter param7 = new SqlParameter("#r", "");
param7.Direction = System.Data.ParameterDirection.Output;
you shouldn't get the return value of the stored procedure; you should take the result set.
Try this:
string str = db.Database.SqlQuery<string>("sp_GetNotesData #StoreNum, #CustomerID, #r", param4, param5, param7).First();
I created a procedure that returns the ID of the Question based on the input text
ALTER PROCEDURE [dbo].[GetQuestionIDbyTekst]
(
#Tekst nvarchar(100)
)
AS
DECLARE #QuestionID int
SELECT QuestionID
FROM dbo.Questions
WHERE Tekst = #Tekst
RETURN #QuestionID
and I have a problem in getting the value of the QuestionID:
public static int getQuestionID(Question p)
{
using (Entities dm = new Entities())
{
return dm.GetQuestionIDbyTekst(p.Tekst);
}
}
Make the #QuestionID as Output parameter. Also you need to assign the result to #QuestionID
ALTER PROCEDURE [dbo].[GetQuestionIDbyTekst]
(
#Tekst nvarchar(100),
#QuestionID INT OUTPUT
)
AS
BEGIN
DECLARE #QuestionID int
SELECT #QuestionID = QuestionID FROM dbo.Questions WHERE Tekst = #Tekst
END
please try this:
ALTER PROCEDURE [dbo].[GetQuestionIDbyTekst]
(
#Tekst nvarchar(100)
)
AS
-- DECLARE #QuestionID int
SELECT QuestionID
FROM dbo.Questions
WHERE Tekst = #Tekst
-- RETURN #QuestionID
You can use your variant of the stored procedure.
And if you use ADO.NET and want to get return value, try this:
SqlConnection con = new SqlConnection(#"Data Source=localhost\***;Initial Catalog=***;Integrated Security=True;Persist Security Info=False;");
con.Open();
SqlCommand cmd = new SqlCommand("GetQuestionIDbyTekst", con);
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.Add("#Tekst", System.Data.SqlDbType.NVarChar).Value = "eee";
SqlParameter returnPar = new SqlParameter();
returnPar.Direction = ParameterDirection.ReturnValue;
cmd.Parameters.Add(retturnPar);
cmd.ExecuteScalar();
var result = returnPar.Value;
If you use Entity Framework, you may use this variant:
public static int GetQuestionIDbyTekst(string question)
{
using (var context = new EfDbContext())
{
var test = new SqlParameter("#Tekst", question);
var resultParam = new SqlParameter("#result", SqlDbType.Int);
resultParam.Direction = ParameterDirection.Output;
context.Database.ExecuteSqlCommand("exec #result = [dbo].[testProc] #Tekst", resultParam, test);
return (int)resultParam.Value;
}
}
I have a classic ASP site, that I am slowly upgrading. I would like to create a function to securely update a SQL database without specifying parameters manually. Something just a tad more dynamic.
(I do not want to use entity framework or Linq)
Here is the code so far:
string updateSql = "UPDATE sometable" + "SET test1= #testData1 " + "WHERE a = #aData1";
SqlCommand UpdateCmd = new SqlCommand(updateSql, conn);
UpdateCmd.Parameters.Add("#testData1 ", SqlDbType.NVarChar, 10, "testData1 ");
UpdateCmd.Parameters.Add("#aData1", SqlDbType.NVarChar, 20, "aData1");
UpdateCmd.Parameters["#testData1 "].Value = "21515";
UpdateCmd.Parameters["#aData1"].Value = "32t3t";
UpdateCmd.ExecuteNonQuery();
pseudo-code (what I would like to achieve)
Create an Ilist covering all variables {get; set:} [validate type/length here]
For every variable that contains a value (without validation issues) create sql update string.
Execute it.
Possible problem:
The only problem I can foresee, is that the list may have 500 variables, but each SQL update may only have only 2 or 3 columns being updated. Is this not efficient?
you need to do something like this....needs more coding obviously....
static void Main(string[] args)
{
var values = new Dictionary<string, object>( );
values.Add( "name", "timmerz" );
values.Add( "dob", DateTime.Now );
values.Add( "sex", "m" );
SqlUpdate( "sometable", values );
}
public static void SqlUpdate( string table, Dictionary<string,object> values, string where )
{
var equals = new List<string>( );
var parameters = new List<SqlParameter>( );
var i = 0;
foreach( var item in values )
{
var pn = "#sp" + i.ToString( );
equals.Add( string.Format( "{0}={1}", item.Key, pn ) );
parameters.Add( new SqlParameter( pn, item.Value ) );
i++;
}
string command = string.Format( "update {0} set {1} where {2}", table, string.Join( ", ", equals.ToArray( ) ), where );
var sqlcommand = new SqlCommand(command);
sqlcommand.Parameters.AddRange(parameters.ToArray( ) );
sqlcommand.ExecuteNonQuery( );
}
I'm not sure I fully understand what you're trying to do, but this might be close to what you're looking for. You can create an arbitrarily long list of parameters and respective values, then build the corresponding UPDATE dynamically from that list.
//set up SqlCommand
SqlCommand UpdateCmd = new SqlCommand();
UpdateCmd.Connection = conn;
//build your dictionary (probably happens elsewhere in your code)
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("col1", "col1 value");
parameters.Add("col2", 42);
parameters.Add("col3", DateTime.Now);
//build a command string and add parameter values to your SqlCommand
StringBuilder builder = new StringBuilder("UPDATE sometable SET ");
foreach(KeyValuePair<string, object> parameter in parameters) {
builder.Append(parameter.Key).Append(" = #").Append(parameter.Key).Append(",");
UpdateCmd.Parameters.AddWithValue("#" + parameter.Key, parameter.Value);
}
builder.Remove(builder.Length - 1,1);
//set the command text and execute the command
UpdateCmd.CommandText = builder.ToString();
UpdateCmd.ExecuteNonQuery();
If you are using SQL Server 2008 you have the option of passing in the parameters and their values as a table to a Stored Procedure.
Inside the Stored Procedure you can join the table to be updated with the table passed in. That would probably be more efficient than creating hundreds of sep update statements.
Here is a link that may help http://msdn.microsoft.com/en-us/library/bb675163.aspx
And here is some sample code based on the code you posted in your question
First Create a table to play with and populate it with some data
CREATE TABLE [dbo].[sometable](
[Test1] [nvarchar](100) NULL,
[a] [nvarchar](100) NULL
) ON [PRIMARY]
Insert sometable Select 'rerere', '122342'
Insert sometable Select 'sfsfw', '343'
Insert sometable Select 'sfdrgss', '434545'
Insert sometable Select 'srgegrgeg', '3939932'
Then Create the Type in SQL Server
Create TYPE dbo.ParamsType AS TABLE
( Test1 nvarchar(100), a nvarchar(100) )
Then Create the Stored Procedure that accepts the type as a parameter
CREATE PROCEDURE usp_UpdateSomeTable
#Parameters dbo.ParamsType READONLY
AS
BEGIN
SET NOCOUNT ON;
UPDATE sometable
SET sometable.Test1 = p.Test1
FROM sometable INNER JOIN #Parameters as p
ON sometable.a = p.a;
END
GO
To test from SQL Server Management Studio you can run
Declare #t as ParamsType
Insert #t Select 'newValue1', '122342'
Insert #t Select 'morenew ', '343'
Insert #t Select 'again', '434545'
Insert #t Select 'OnceMore', '3939932'
exec usp_UpdateSomeTable #Parameters=#t
To Test from C# Try
static void Main(string[] args)
{
System.Data.DataTable YourData = new DataTable("Parameters");
DataColumn column;
DataRow row;
column = new DataColumn();
column.DataType = System.Type.GetType("System.String");
column.ColumnName = "Test1";
YourData.Columns.Add(column);
column = new DataColumn();
column.DataType = System.Type.GetType("System.String");
column.ColumnName = "a";
YourData.Columns.Add(column);
row = YourData.NewRow();
row["Test1"] = "newValue1";
row["a"] = "122342";
YourData.Rows.Add(row);
row = YourData.NewRow();
row["Test1"] = "morenew";
row["a"] = "343";
YourData.Rows.Add(row);
row = YourData.NewRow();
row["Test1"] = "again";
row["a"] = "434545";
YourData.Rows.Add(row);
SqlConnectionStringBuilder connString = new SqlConnectionStringBuilder();
connString.DataSource = "127.0.0.1";
connString.InitialCatalog = "SO";
connString.IntegratedSecurity = true;
using (SqlConnection conn = new SqlConnection())
using (SqlCommand cmd = new SqlCommand())
{
cmd.CommandText = "usp_UpdateSomeTable";
cmd.CommandType = System.Data.CommandType.StoredProcedure;
SqlParameter p = cmd.Parameters.AddWithValue("#Parameters", YourData);
p.SqlDbType = SqlDbType.Structured;
p.TypeName = "dbo.ParamsType";
cmd.Connection = conn;
conn.ConnectionString = connString.ConnectionString;
conn.Open();
cmd.ExecuteNonQuery();
}
}