I have a list of CheckBoxes:
List<CheckBox> checkBoxes = new List<CheckBox>();
I want to update it via a stored procedure, so I have:
private void btnSave_Click(object sender, EventArgs e)
{
SQLConnMgr db = new SQLConnMgr();
foreach (var c in checkBoxes)
{
db.ExeSQL($"exec test #CheckBoxName = {c.Name}, #CheckBoxValue = {c.Checked} ");
}
}
Stored procedure:
CREATE OR ALTER PROCEDURE test
-- Add the parameters for the stored procedure here
#CheckBoxName VARCHAR(255),
#CheckBoxValue BIT
AS
BEGIN
SET NOCOUNT ON;
-- Insert statements for procedure here
UPDATE MyTable SET #CheckBoxName = #CheckBoxValue
END
My question is: is there another way to do this? Like sending multiple petitions in the foreach statement instead of only one at a time?
UPDATE
So to be more clear every bool is a column so I need something like:
DECLARE #CurrentCheckboxName VARCHAR(255) = (SELECT
[CheckBoxName]
FROM #CheckBoxList)
UPDATE [m]
SET
#CurrentCheckboxName = [c].[CheckBoxValue]
FROM [RedMarkItems] [m]
JOIN #CheckBoxList [c] ON [c].[CheckBoxName] = #CurrentCheckboxName
but how can iterate on each checkboxName in my DECLARE?
Depending on the version of SQL Server you are using, you could use a TABLE parameter type for your stored proc and call it only once.
CREATE TYPE dbo.MyCheckBoxValues AS TABLE(
CheckBoxName VARCHAR(255) NOT NULL,
CheckBoxValue BIT NOT NULL )
Then you modify your stored proc to use the type.
CREATE OR ALTER PROCEDURE test
-- Add the parameters for the stored procedure here
#CheckBoxList MyCheckBoxValues READONLY
AS
BEGIN
SET NOCOUNT ON;
-- Insert statements for procedure here
UPDATE m SET CheckBoxValue=c.CheckBoxValue
FROM MyTable m
JOIN #CheckBoxList c ON c.CheckBoxName=m.CheckBoxName
END
You can also use Dynamic SQL in your stored proc. For Each checkboxValues :
DECLARE #Query nvarchar(max);
SET #Query = 'UPDATE Table SET ' + #CheckboxName + '='+ #CheckBoxValue;
exec sp_executeSql #Query
Then you only have to get the values in your code.
Something like this should do it.
StringBuilder builder = new StringBuilder();
builder.Append("DECLARE #MyCheckboxes MyCheckBoxValues; ");
foreach (Guid id in _equipmentToMerge)
{
builder.Append(String.Format("INSERT INTO #MyCheckboxes (CheckBoxName, CheckBoxValue) VALUES ('{0}',{1}); ", name, ischecked));
}
builder.Append("exec dbo.test #MyCheckboxes ");
I think there is a problem with your Update statement in your stored procedure. Could you change it thusly?
Update MyCheckBoxValues SET CheckBoxValue = #CheckBoxValue Where CheckBoxName = #CheckBoxName
I don't know why you want to complicate things. Use a single connection for the loop and I can't imagine that you could have a prohibitive number of check boxes on your form. If it is still too slow, get rid of entity framework or whatever orm you are using and try dealing with the server directly.
Related
Hey all I am trying to figure out how to go about this. I am wanting to send a parameter that is the name of my table to a query in my C# program. I've read that this is not possible and they suggested that you make a stored procedure to do this.
So this is my code so far:
CREATE PROCEDURE _tmpSP
#TableName NVARCHAR(128)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #Sql NVARCHAR(MAX);
SET #Sql = N'SELECT TOP 1 HelpMsg FROM ' + QUOTENAME(#TableName)
EXECUTE sp_executesql #Sql
DROP PROCEDURE [_tmpSP]
END
When I execute that in Server Management Studio it creates the SP but never executes that store procedure nor deletes it.
When I run that SP in Server Management Studio (right-clicking on it under Programmability>dbo._tmpSP and choosing Execute Stored Procedure) and give it the table name, it populates and then deletes the SP. This is the end result I want without having to make 2 query's.
The SQL query for when the SP runs is this (tHelp being the table name):
USE [TTool]
GO
DECLARE #return_value int
EXEC #return_value = [dbo].[_tmpSP]
#TableName = N'tHelp'
SELECT 'Return Value' = #return_value
GO
I get the returned help message and also returned value 0.
How can I modify this SP in order to do that?
Just do this, forget stored procedures:
EXECUTE sp_executesql 'SELECT TOP 1 HelpMsg FROM '+QUOTENAME(#TableName)
Dirty C#...
string qry = string.Format("SELECT TOP 1 HelpMSG FROM {0}", myTableName.Replace("'", "''"));
cmd = conn.CreateCommand();
cmd.CommandText = qry;
string helpMsg = conn.ExecuteScalar();
Where conn is an instance of System.Data.SqlClient.SqlConnection
I agree with #SsJVasto. If you still need your query not be hard coded in the C# program you can use an xml and keep the query in it. And fetch the xml and execute the query. I guess you would like to handle some dynamic stuff.
There is no point in doing this because there is quite complicated and also incurs the overhead of creating and dropping of the stored procedure. If you have a dynamic query that deals with some dynamic elements that cannot be pushed as parameters, you can construct the query string:
var query = $"SELECT TOP 1 col FROM {tableName}";
However, you must take care to avoid SQL injection if tableName is constructed based on user input. This question and its answers deal with this problem:
DbConnection connection = GetMyConnection();
DbProviderFactory factory = DbProviderFactories.GetFactory(connection);
DbCommandBuilder commandBuilder = factory.CreateCommandBuilder();
var tableName commandBuilder.QuoteIdentifier(rawTableName);
If "normal" (non table name) parameters are needed, pass them as usual within the query. E.g. #param1, #param2
You need create another SP to apply your logic. First let's see your SP:
CREATE PROCEDURE [_tmpSP]
#TableName NVARCHAR(128)
AS
BEGIN
DECLARE #Sql NVARCHAR(MAX);
SET #Sql = N'SELECT TOP 1 HelpMsg FROM ' + #TableName
EXEC(#Sql)
END
Then create another SP only if you need to drop the first one after return the result. The logic will be :
Create procedure auto_delete
#NewTableName
as
begin
EXEC _tmpSP #TableName = #NewTableName
Drop procedure [_tmpSP]
End
In C# (I assume you are using the 2nd SP above):
Your code could be like this:
..
using System.Data.SqlClient;
..
string a = YourTableName;
using (SqlConnection sqlCon = new SqlConnection(YourDatabaseConnection))
{
sqlCon.Open()
using (SqlCommand sqlCmd = sqlCon.CreateCommand())
{
sqlCmd.CommandText = "auto_delete";
sqlCmd.CommandType = CommandType.StoredProcedure;
sqlCmd.Parameters.Add(new SqlParameter("NewTableName", a));
sqlCmd.ExecuteNonQuery();
}
sqlCon.Close();
}
Can I perform a bulk update using a stored procedure that sends data to a temp table from user input which is not in a datatable.
If I have a foreach loop that takes user input such as values from a checkboxlist and text boxes what I want to know is how to parameterize those separate values in my stored procedure or if I can do it in my code. I cannot use table-valued parameters since I'm using a version of SQL that does not support it.
conn.Open();
foreach(ListItem item in CheckBoxList1.Items)
{
if(item.Selected)
{
//handling parameters in loop.
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "Update_Account_Table";
cmd.Parameters["#SeqNum"].Value = amount.Text;
cmd.Parameters["#SeqDate"].Value = DateTime.ParseExact(datepicker.Text, "mmddyyyy", CultureInfo.InvariantCulture);
cmd.Parameters["#Account_ID"].CheckBoxList1.SelectedValue;
cmd.ExecuteNonQuery();
}
conn.Close();
}
Stored procedure
CREATE TABLE TempTable
(
SeqNum int,
SeqDate datetime,
Account_ID varchar(2)
);
CREATE PROCEDURE [ACCOUNTTABLE_UPDATE]
AS
SET NOCOUNT ON
BEGIN
UPDATE AccountTable
SET SeqNum = t.SeqNum, SeqDate = t.SeqDate
FROM AccountTable AT
INNER JOIN TempTable t ON AT.AccountID = t.AccountID
END
This uses a dynamic table name in stored procedure, and sets value based on incoming parameter. You could use code below instead of temp table, or use parameters #seqnum, #SeqDate, #AcctID in place of #Stats_Value below:
USE [EODData]
GO
/****** Object: StoredProcedure [dbo].[erase_Stats] Script Date: 8/23/2016 4:32:55 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[erase_Stats]
#table varchar(25), #stats_value int
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
Declare #ProductsSQL nvarchar(max);
SET #ProductsSQL = 'update ' + #table + ' set stats_completed = #Stats_Value'
exec sp_executesql #ProductsSQL
END
GO
you can add # before the table to make it temporary table
your should be like
CREATE TABLE #TempTable
will create temporary table and destroyed after process completes
I have a collection of record ids(x,y,z,..) which is pass to a stored procedure as a string value. My stored procedure is:
CREATE PROCEDURE [dbo].[Sp_Getvalue](#recordId varchar(30))
AS
BEGIN
SET NOCOUNT ON;
-- Insert statements for procedure here
select
x.record_Id,
x.name,
x.address from dbo.tblvalue x where x.record_Id in (#recordId)
END
in code behind
var obj = context.Sp_Getvalue(value);
I am getting error as
An exception of type 'System.Data.EntityCommandExecutionException' occurred in System.Data.Entity.dll but was not handled in user code
You can not use the parameter directly in IN clause
Please check the following syntax where I used a SQL split string function
CREATE PROCEDURE [dbo].[Sp_Getvalue](#recordId varchar(30))
AS
BEGIN
SET NOCOUNT ON;
select
x.record_Id,
x.name,
x.address
from dbo.tblvalue x
where
x.record_Id in (
select val from dbo.split(#recordId,',')
)
END
You can copy the split function codes from here: http://www.kodyaz.com/articles/t-sql-convert-split-delimeted-string-as-rows-using-xml.aspx
and create on your target database
You cannot directly use the comma separated values in IN clause and no need of any create any functions for that. There are two approaches to do that
1. Use Dynamic Sql to get records according to values in #recordId
CREATE PROCEDURE [dbo].[Sp_Getvalue](#recordId varchar(30))
AS
BEGIN
SET NOCOUNT ON;
DECLARE #query NVARCHAR(MAX)
SET #query = 'INSERT INTO TABLETOINSERT(COL,COL2,COL3)
SELECT record_Id,name,address
FROM tblvalue
WHERE record_Id IN('+#recordId+')'
EXEC SP_EXECUTESQL #query
END
Click here to view result
If you do not want to insert, and just want select statement, remove the
INSERT INTO TABLETOINSERT(COL,COL2,COL3) from the dynamic sql.
2. Using XML format to split comma separated values to row and using IN
CREATE PROCEDURE [dbo].[Sp_Getvalue](#recordId varchar(30))
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO TABLETOINSERT(COL1,COL2,COL3)
SELECT record_Id,NAME,[ADDRESS]
FROM tblvalue
WHERE record_Id IN
(
-- Convert comma separated values to rows
SELECT LTRIM(RTRIM(Split.a.value('.', 'VARCHAR(100)'))) 'KeyWords'
FROM
(
SELECT CAST ('<M>' + REPLACE(#recordId, ',', '</M><M>') + '</M>' AS XML) AS Data
) AS A
CROSS APPLY Data.nodes ('/M') AS Split(a)
)
END
Click here to view result
I looked around for a similar question (I'm sure there is one somewhere) but could not find one.
I have a list of IDs that for each of the IDs I need to update another column of that IDs row to the same string.
Essentially, I want something like this:
List<int> uniqueIDs;
UPDATE my_table
SET certainColumn = "foo bar"
WHERE uniqueID = uniqueIDs[0]
OR uniqueID = uniqueIDs[1]
...
OR uniqueID = uniqueID[uniqueIDs.Length-1]
I know this could be achieved by surrounding this in a for/foreach-loop, but I was wondering if there is a better way to get this done, possibly in one database connection?
Any help is greatly appreciated.
Well, you could use a TVP. First, create this on your server:
CREATE TYPE dbo.UniqueIDs AS TABLE(ID INT PRIMARY KEY);
Then a stored procedure:
CREATE PROCEDURE dbo.UpdateByID
#tvp dbo.UniqueIDs READONLY
AS
BEGIN
SET NOCOUNT ON;
UPDATE t SET certainColumn = 'foo bar'
FROM dbo.my_table AS t
INNER JOIN #tvp AS tvp
ON t.uniqueID = tvp.ID;
END
Or:
CREATE PROCEDURE dbo.UpdateByID
#tvp dbo.UniqueIDs READONLY
AS
BEGIN
SET NOCOUNT ON;
UPDATE t SET certainColumn = 'foo bar'
FROM dbo.my_table AS t
WHERE EXISTS (SELECT 1 FROM #tvp
WHERE ID = t.uniqueID);
END
Then instead of a List use a DataTable to hold your IDs in your C# application, and call the stored procedure, passing #tvp as a Structured parameter. I have simple examples of the C# side posted all over this site:
How to pass an array into a SQL Server stored procedure
Insert entire DataTable into database at once instead of row by row?
Parameters to the EXISTS clause in a stored procedure
var query = "UPDATE my_table
SET certainColumn = 'foo bar'
WHERE uniqueID in (" + String.Join(",", uniqueIDs) + ")"
I guess you could do something like this. Get your List of Ids in some TempTable or table variable and use IN operator in your update statement. something like this .....
UPDATE my_table
SET certainColumn = 'foo bar'
WHERE uniqueID IN (SELECT uniqueID
FROM #List_Table)
I have a Vendor object within my View Model. When I insert this vendor record into the database, I want to retrieve the ID of this Vendor because it will be used as a foreign key on another table immediately after (in a different sproc). I'm attempting to do this with test as the ID I need to retrieve. The following code doesn't work because proc_amcInsertApplicationServerRelationship expects an integer, but test is of type of Object Parameter.
I guess my questions are:
Am I approaching this correctly? If not, what would be a better approach? Also, based on my current approach, is there something simple I'm overlooking that I could do to get this to work? Here's my code (sorry if I'm not providing enough detail):
[HttpPost]
public ActionResult Create(ApplicationViewModel applicationViewModel)
{
try
{
// TODO: Add insert logic here
ObjectParameter test = new ObjectParameter("ID", typeof (int));
var vendorID = db.proc_amcInsertNewVendor(applicationViewModel.Vendor.Company, applicationViewModel.Vendor.StreetAddress, applicationViewModel.Vendor.SecondaryStreetAddress,
applicationViewModel.Vendor.City, applicationViewModel.Vendor.State, applicationViewModel.Vendor.ZipCode, applicationViewModel.Vendor.PhoneNumber,
applicationViewModel.Vendor.Website, test);
foreach (var serverID in applicationViewModel.ServerIDs)
{
db.proc_amcInsertApplicationServerRelationship(test, serverID);
}
return RedirectToAction("Index");
}
catch (Exception exception)
{
return View();
}
}
EDIT: Per Request, here's my stored procedure.
#Company varchar(100)
,#StreetAddress varchar(100)
,#SecondaryStreetAddress varchar(50)
,#City varchar(50)
,#State varchar(50)
,#ZipCode varchar(10)
,#PhoneNumber varchar(15)
,#Website varchar(200)
,#ID int = NULL OUT
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
INSERT INTO [dbo].[amc_Vendors]
(
[Company]
,[StreetAddress]
,[SecondaryStreetAddress]
,[City]
,[State]
,[ZipCode]
,[PhoneNumber]
,[Website]
)
VALUES
(
#Company
,#StreetAddress
,#SecondaryStreetAddress
,#City
,#State
,#ZipCode
,#PhoneNumber
,#Website
)
SET #Id = SCOPE_IDENTITY()
END
GO
In your SP after insert statement. Use
Select ##IDENTITY As ReturnedId
In your service code, do:
int Id=db.proc_amcInsertApplicationServerRelationship(serverId).FirstOrDefault().ReturnedId;
Use this Id for your further processing.
There is solution to this but you need to make sure for the followings:
-What you can try with your current code is return Identity of recently added Items with ##Identity in SQL procedure and get the same from command as return value or as output parameter.
Use the returned value and sent the same in next operation.
But as far as my expertise says this would be not good option. Since you are having dependencies for Query execution, You need to implement Transaction as well.
you can handle them at both database and Application level
Use Single SP to insert records in both table with Transaction or
Use Transaction class at Application level in C#
Hope these would be helpful
Do Following:
In Your SP:
CREATE PROCEDURE dbo.YourSPName
#Param1 varchar(156),
#Id Int OUTPUT
AS
SET NOCOUNT ON;
SELECT #Id = ##IDENTITY
In your C# code:
var outputParameter = new ObjectParameter("Id", typeof(int));
context.YourSPName("ParamValue", outputParameter);
Console.WriteLine(outputParameter.Value);