I want to pass an array of 20k IDs to my stored procedure param in order to update a certain table.
Instead of running 20k update queries separately, I want to run 1 query to update all, it should improve my performances.
Any knows I can I pass a param to my stored proc?
I understood that NVARCHAR(MAX) is limited to 8000 chars, is it possible at all to send such a huge data using stored proc param?
Use a Table Value Parameter instead. See Use Table-Valued Parameters (Database Engine). A TVP is exactly as the name implies: a parameter that is a table. You assign to it from your client code a DataTable and the procedure (or you ad-hoc SQL codE) receives the entire DataTable as a parameter.This is an MSDN copied example:
// Assumes connection is an open SqlConnection.
using (connection)
{
// Create a DataTable with the modified rows.
DataTable addedCategories = CategoriesDataTable.GetChanges(
DataRowState.Added);
// Define the INSERT-SELECT statement.
string sqlInsert =
"INSERT INTO dbo.Categories (CategoryID, CategoryName)"
+ " SELECT nc.CategoryID, nc.CategoryName"
+ " FROM #tvpNewCategories AS nc;"
// Configure the command and parameter.
SqlCommand insertCommand = new SqlCommand(
sqlInsert, connection);
SqlParameter tvpParam = insertCommand.Parameters.AddWithValue(
"#tvpNewCategories", addedCategories);
tvpParam.SqlDbType = SqlDbType.Structured;
tvpParam.TypeName = "dbo.CategoryTableType";
// Execute the command.
insertCommand.ExecuteNonQuery();
}
Related
I have a SQL server database of a web application, my requirement is to read 2-3 table data from one source database and insert the data in the destination database. My input will be a Name and an ID, based on that I have to read data from the source database and I have to validate whether the similar Name already exists in the destination database. I have to do this via a C# windows application or a web application.
So far in my research, people have recommended using SqlBulkCopy or an SSIS package, I tried to transfer one table data using the following code.
using (SqlConnection connSource = new SqlConnection(csSource))
using (SqlCommand cmd = connSource.CreateCommand())
using (SqlBulkCopy bcp = new SqlBulkCopy(csDest))
{
bcp.DestinationTableName = "SomeTable";
cmd.CommandText = "myproc";
cmd.CommandType = CommandType.StoredProcedure;
connSource.Open();
using (SqlDataReader reader = cmd.ExecuteReader())
{
bcp.WriteToServer(reader);
}
}
The problem I'm facing is that, I have to copy 2 table data, based on table 1, table 2 value like ID(primary key) changes, I have to update this in the SqlDataReader, so in order to get this new ID in the destination database, I have to insert one table data first, then get the ID, then update this ID in my reader object and then do another SqlBulkCopy, this doesn't look like the ideal way to do this, is there any other to do this?
On the source SQL instance I would create a linked server referencing destination/target SQL instance and then I would create a stored procedure within source database thus:
USE SourceDatabase
GO
CREATE PROCEDURE dbo.ExportSomething
#param1 DataType1,
#param2 DataType2, ...
AS
BEGIN
INSERT LinkedServer.DestinationDatabase.dbo.TargetTable
SELECT ...
FROM dbo.Table1 INNER JOIN dbo.Table2 ....
WHERE Col1 = #param1 AND/OR ...
END
GO
Then, the final step is to call this stored procedure from client application.
I can't see where i'm going wrong and was wondering if you could help at all?
Just a basic SELECT with a table.
With regards to the error message, i thought i was declaring the #tableName variable in the parameters section?
SqlDataAdapter adapter = new SqlDataAdapter(
"SELECT * FROM #tableName",con);
adapter.SelectCommand.Parameters.Add(new SqlParameter
{
ParameterName = "#tableName",
Value = tableName,
SqlDbType = SqlDbType.NVarChar
});
adapter.Fill(databaseList);
You cannot pass table names to SELECT as a parameter. Construct your SQL dynamically, by inserting the properly quoted (escaped) table name in the SQL string.
From clause not be expression so, cant send parameter
Try this
SqlDataAdapter adapter = new SqlDataAdapter(string.Format("Select * From {0}", "yourTableName"), con);
I have an app that makes a bunch of updates to objects hydrated with data from an SQL Server table and then writes the updates objects' data back to the DB in one query. I'm trying to convert this into a parameterized query so that I don't have to do manual escaping, conversions, etc.
Here's the most straightforward example query:
UPDATE TestTable
SET [Status] = DataToUpdate.[Status], City = DataToUpdate.City
FROM TestTable
JOIN
(
VALUES --this is the data to parameterize
(1, 0, 'A City'),
(2, 0, 'Another City')
) AS DataToUpdate(Id, [Status], City)
ON DataToUpdate.Id = TestTable.Id
I've also played around with using OPENXML to do this, but I'm still forced to write a bunch of escaping code when adding the values to the query. Any ideas on how to make this more elegant? I am open to ADO.NET/T-SQL solutions or platform-agnostic solutions.
One thought I had (but I don't really like how dynamic this is) is to dynamically create parameters and then add them to an ADO.NET SqlConnection, e.g.
for(int i = 0; i < data.Length; i++)
{
string paramPrefix = string.Format("#Item{0}", i);
valuesString.AppendFormat("{0}({1}Status)", Environment.NewLine, paramPrefix);
var statusParam = new SqlParameter(
string.Format("{0}Status", paramPrefix),
System.Data.SqlDbType.Int)
{ Value = data[i].Status };
command.Parameters.Add(statusParam);
}
I'm not exactly sure how you store your application data (and I don't have enough rep points to post comments) so I will ASSUME that the records are held in an object CityAndStatus which is comprised of int Id, string Status, string City held in a List<CityAndStatus> called data. That way you can deal with each record one at a time. I made Status a string so you can convert it to an int in your application.
With those assumptions:
I would create a stored procedure https://msdn.microsoft.com/en-us/library/ms345415.aspx in SQL Server that updates your table one record at at time.
CREATE PROCEDURE updateCityData (
#Id INT
,#Status INT
,#City VARCHAR(50)
)
AS
BEGIN TRAN
UPDATE TestTable
SET [Status] = #Status
,City = #City
WHERE Id = #Id
COMMIT
RETURN
GO
Then I would call the stored procedure https://support.microsoft.com/en-us/kb/310070 from your ADO.NET application inside a foreach loop that goes through each record that you need to update.
SqlConnection cn = new SqlConnection(connectionString);
cn.Open();
foreach (CityAndStatus item in data)
{
SqlCommand cmd = new SqlCommand("updateCityData",cn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#Id", item.Id);
cmd.Parameters.AddWithValue("#Status", Convert.ToInt32(item.Status));
cmd.Parameters.AddWithValue("#City", item.User);
cmd.ExecuteNonQuery();
cmd.Dispose();
}
cn.Close();
After that you should be good. The one thing left that might stand in your way is SQL Server makes web application users grant permission to execute stored procedures. So in SQL Server you may have to do something like this to allow your application to fire the stored proc.
GRANT EXECUTE
ON updateCityData
TO whateverRoleYouHaveGivenPermissionToExecuteStoredProcedures
Good Luck
If any, what is the difference between the following ways passing parameters.
SQLStr = "SELECT * FROM TABLE_NAME WHERE ID = ? "
command = new oleDbCommand(SQLStr, conn)
Command.Parameters.AddWithValue("#ID", Request.Querystring("ID"))
Vs.
SQLStr = "SELECT * FROM TABLE_NAME WHERE ID = #ID "
Command = new oleDbCommand(SQLStr, conn)
Command.Parameters.AddWithValue("#ID", Request.Querystring("ID"))
Maybe not in this example but could these two methods have different meanings? Perhaps when I need to pass the same value twice and I would be tempted to use the same variable name?
Thanks.
OleDbCommand does not support named parameters. Even if you use named parameter with # in your current query, their order will only matter. Currently you have only one parameter so you won't see the difference.
See: OleDbCommand.Parameters Property
The OLE DB .NET Provider does not support named parameters for passing
parameters to an SQL statement or a stored procedure called by an
OleDbCommand when CommandType is set to Text. In this case, the
question mark (?) placeholder must be used. For example:
SELECT * FROM Customers WHERE CustomerID = ?
Therefore, the order in which OleDbParameter objects are added to
the OleDbParameterCollection must directly correspond to the position
of the question mark placeholder for the parameter in the command
text.
Consider the following examples with multiple parameters:
SQLStr = "SELECT * FROM TABLE_NAME WHERE ID = #ID AND NAME = #Name";
Command = new oleDbCommand(SQLStr, conn);
Command.Parameters.AddWithValue("#Name", "ABC");
Command.Parameters.AddWithValue("#ID", Request.Querystring("ID")); //'A1'
Since #Name is added before #ID in the parameter collection, the query would look like :
SELECT * FROM TABLE_NAME WHERE ID = 'ABC' AND NAME = 'A1`; //assuming ID is A1
Note that ID got the value of NAME parameter and so as NAME got the value of ID, which is wrong.
If in the stored procedure, I just execute one statement, select count(*) from sometable, then from client side (I am using C# ADO.Net SqlCommand to invoke the stored procedure), how could I retrieve the count(*) value? I am using SQL Server 2008.
I am confused because count(*) is not used as a return value parameter of stored procedure.
thanks in advance,
George
Either you use ExecuteScalar as Andrew suggested - or you'll have to change your code a little bit:
CREATE PROCEDURE dbo.CountRowsInTable(#RowCount INT OUTPUT)
AS BEGIN
SELECT
#RowCount = COUNT(*)
FROM
SomeTable
END
and then use this ADO.NET call to retrieve the value:
using(SqlCommand cmdGetCount = new SqlCommand("dbo.CountRowsInTable", sqlConnection))
{
cmdGetCount.CommandType = CommandType.StoredProcedure;
cmdGetCount.Parameters.Add("#RowCount", SqlDbType.Int).Direction = ParameterDirection.Output;
sqlConnection.Open();
cmdGetCount.ExecuteNonQuery();
int rowCount = Convert.ToInt32(cmdGetCount.Parameters["#RowCount"].Value);
sqlConnection.Close();
}
Marc
PS: but in this concrete example, I guess the alternative with just executing ExecuteScalar is simpler and easier to understand. This method might work OK, if you need to return more than a single value (e.g. counts from several tables or such).
When you execute the query call ExecuteScalar - this will return the result.
Executes the query, and returns the first column of the first row in the result set returned by the query. Additional columns or rows are ignored.
Since you are only returning one value this would return just the value from your count expression. You will need to cast the result of this method to an int.
marc_s answer worked fine for integer. but for varchar the lenght must be specifed.
cmdGetCount.Parameters.Add("#RowCount", SqlDbType.varchar,30).Direction = ParameterDirection.Output;