I am getting this error in Visual Studio for this part of my code. Why? I do declare #TableName below as FormField.
Must declare the table variable "#TableName".
Code:
using (SqlCommand sqlCmd2 = new SqlCommand())
{
sqlCmd2.Connection = sqlConn2;
sqlCmd2.CommandType = System.Data.CommandType.Text;
sqlCmd2.CommandText = string.Format("SELECT DisplayName AS MyColumn FROM #TableName WHERE EventId = 1 AND Visible = 1");
sqlCmd2.Parameters.Add("#TableName", SqlDbType.NVarChar).Value = "FormField";
sqlCmd2.ExecuteNonQuery();
}
The table name can not be resolved by using parameter. For this purpose you have to prepare your statement when passing to SqlCommand:
string tableName = "FormField";
using (SqlCommand sqlCmd2 = new SqlCommand())
{
sqlCmd2.Connection = sqlConn2;
sqlCmd2.CommandType = System.Data.CommandType.Text;
sqlCmd2.CommandText = string.Format("SELECT DisplayName AS MyColumn FROM {0} WHERE EventId = 1 AND Visible = 1", tableName );
sqlCmd2.ExecuteNonQuery();
}
SqlParameter can only be used for passing parameter. E.g. in insert or update statements. But if you want to do this, be sure tableName can not be changed from outside your source code to prevent from sql injection in any case. Youre maybe able to filter acceptable values before executing any queries.
You cannot specify a table name (or column name or function or operator) as a parameter.
Hence, in your query, #TableName is interpreted as a table variable, rather than a parameter and the table variable is not defined.
Alas, you need to put the table in explicitly, using string operations.
Related
I have an insert query like the below. However, scopeIdentity does not return 42, it returns 1042.
This is the SQL Server table:
My code:
int masterId = 0;
using (SqlConnection cmd = new SqlConnection(connString))
{
using (SqlCommand conn = cmd.CreateCommand())
{
conn.CommandText = "INSERT INTO[MasterReportData]([ReportName],[CaseList],[EmployeeId],[datetime]) VALUES(#reportName, #caseList, #employeeId, #datetime) SET #ID = SCOPE_IDENTITY()";
conn.Parameters.Add("#reportName", SqlDbType.VarChar).Value = reportName;
conn.Parameters.Add("#caseList", SqlDbType.VarChar).Value = caseList;
conn.Parameters.Add("#employeeId", SqlDbType.Char).Value = employeeId;
conn.Parameters.Add("#datetime", SqlDbType.DateTime).Value = datetime;
conn.Parameters.Add("#ID", SqlDbType.Int).Direction = ParameterDirection.Output;
cmd.Open();
conn.ExecuteNonQuery();
masterId = Convert.ToInt32(conn.Parameters["#ID"].Value);
cmd.Close();
}
}
Have a look at https://learn.microsoft.com/en-us/sql/t-sql/functions/scope-identity-transact-sql
The description says:
Returns the last identity value inserted into an identity column in the same scope. A scope is a module: a stored procedure, trigger, function, or batch. Therefore, if two statements are in the same stored procedure, function, or batch, they are in the same scope.
In words: it Returns the last id and not the next to use. Therefore you can't use the INSERT command like that. What you can do is:
Configure your ID as auto-increment id. Then run the INSERT command and run SELECT SCOPE_IDENTITY() afterwards to find out which ID was used.
I'm trying to follow best practice (and also remove Visual Studio Code Analysis warnings) by using parameters when dropping a SQL Server index.
Not using parameters works fine:
string tableName = "dbo.TableName";
SqlCommand sqlCommand = new SqlCommand("DROP INDEX Blah ON " + tableName);
sqlCommand.Connection = sqlConnection;
sqlCommand.ExecuteNonQuery();
However, when I try to use a parameter I get an error
Incorrect syntax near '#TableName'.
Code:
string tableName = "dbo.TableName";
SqlCommand sqlCommand = new SqlCommand("DROP INDEX Blah ON #TableName");
sqlCommand.Parameters.Add(new SqlParameter("TableName", tableName));
sqlCommand.Connection = sqlConnection;
sqlCommand.ExecuteNonQuery();
What am I doing wrong?
You are doing nothing wrong. Parameters cannot be used to replace identifiers -- column names/aliases, table names/aliases, schema names, and database names. They also cannot be used to replace function names or operators or keywords.
That is a long list. They can be used to replace constants in the query.
I guess the way to remember this is that the parameterized query can be pre-compiled. In order to compile a query, all object references need to be resolved -- so the values cannot be provided by parameters.
You have already solved the problem by putting the table in the string. You can use quotename() to help protect against injection (see here).
DROP INDEX is a DDL statement, most DDL statements don't accept parameterized values. The best you can do is use dynamically constructed SQL and escape the table name using QUOTENAME
string tableName = "dbo.TableName";
string sql = #"
declare #sql nvarchar(500)
set #sql = N'DROP INDEX Blah ON ' + QUOTENAME(#TableName)
exec sp_executesql #sql
";
SqlCommand sqlCommand = new SqlCommand("");
sqlCommand.Parameters.Add("#TableName", SqlDbType.NVarChar, 50).Value = tableName;
sqlCommand.Connection = sqlConnection;
sqlCommand.ExecuteNonQuery();
I also updated your code to use the more "normal" way to add a parameter, explicitly setting the datatype of the parameter.
I am inserting a record in DB, I am using inline query, I have to get the generated ID, this is how I am doing this
var query = "INSERT INTO [Users] ([Username],[Password]) VALUES(#username,#pwd) SELECT #ID = SCOPE_IDENTITY()";
OleDbParameter[] queryparam = new OleDbParameter[3];
queryparam[0] = new OleDbParameter("#username", OleDbType.VarChar);
queryparam[0].Value = "username";
queryparam[1] = new OleDbParameter("#pwd", OleDbType.VarChar);
queryparam[1].Value = "123456";
queryparam[2] = new OleDbParameter("#ID", OleDbType.Integer);
queryparam[2].Direction = ParameterDirection.Output;
OleDbCommand myCommand = new OleDbCommand();
myCommand.Connection = DBConnectionHelper.getConnection();
myCommand.CommandText = query;
myCommand.Parameters.AddRange(queryparam);
adapter.InsertCommand = myCommand;
myCommand.ExecuteNonQuery();
I am getting error:
Must declare the scalar variable "#ID".
Must declare the scalar variable "#username".
I also tried using (int)myCommand.ExecuteScalar() but no luck.
How can I get generated ID in this case?
From OleDbCommand.Parameters:
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 Text1. In this case, the question mark (?) placeholder must be used. For example:
SELECT * FROM Customers WHERE CustomerID = ?
So, for your code your query should be:
INSERT INTO [Users] ([Username],[Password]) VALUES(?,?) SELECT ? = SCOPE_IDENTITY()
1Which is the default, so is what your code is using.
From what I can make out of your question, you have declared a variable called query and written your query inside it. Also, your parameter declaration seems to be correct.
Now the problem might be in this line :
myCommand.CommandText = _query;
The variable is query and here you are using _query. So that is probably why the parameters are not added for that particular query and hence the error. It should be like this :
myCommand.CommandText = query;
Hope this clears.
Instead of Select Use Set #Id=Scope_Identity()
using (SqlCommand cmd = new SqlCommand())
{
DataTable dt = new DataTable();
cmd.CommandText = p;
cmd.CommandType = CommandType.Text;
SqlConnection con = new SqlConnection("--");
cmd.Connection = con;
cmd.Connection.Open();
foreach (var parameter in filters)
{
var type = parameter.Value.GetType();
var param = new SqlParameter(parameter.Id, parameter.Value);
param.Direction = ParameterDirection.Input;
param.Value = parameter.Value;
cmd.Parameters.Add(param);
}
dt.Load(cmd.ExecuteReader());
cmd.Connection.Close();
return dt;
}
Here is my code.
Variable "p" is my sqlQuery string.
Variable "filters" is my parameter list.
For example: parameter.Id = "#offerId" (as string) and parameter.Value = 1230 (as Integer)
Also my query is like that : "select * from Offers where ID = #offerID and IsActive = #isActive"
when pass into cmd.ExecuteReader(), in IntelliTrace shows my query like that:
--The data may be truncated and may not represent the query that was run on the server
USE [DB];
GO
--Type and value data was not available for the following variables. Their values have been set to defaults.
DECLARE #offerID AS SQL_VARIANT;
DECLARE #isActive AS SQL_VARIANT;
SET #offerID = NULL;
SET #isActive = NULL;
select * from Offers where ID = #offerID and IsActive = #isActive
We tried lots of method for set. But always variables set null.
IntelliTrace currently only supports this kind of type information for log files from MMA scenarios. In your scenario, the type information from SqlParameter isn't collected; as a result all the variables in the query default to SQL_VARIANT with null values.
Using your code on Visual Studio 2010 and SQL Server 2008 I tried to reproduce your scenario (or at least what I thought your scenario was as you weren't very specific).
I created these tables: Q25682067 using int for the offer id and bit for the 'isActive' field; and Q25682067_Offers with sql_variant for each field, as suggested by your post.
CREATE TABLE [Q25682067]([ID] [int] NOT NULL, [IsActive] [bit] NOT NULL) ON [PRIMARY]
CREATE TABLE [Q25682067_Offers]([ID] [sql_variant] NOT NULL,[IsActive] [sql_variant] NOT NULL ) ON [PRIMARY]
Data pairs (1,false) and (1,true) added to each table.
Now considering your filters are something like:
var filters = new Parameter[] {
new Parameter() {Id="#offerID ", Value=1},
new Parameter() {Id="#isActive", Value=false}
};
where a Parameter might very swiftly be (without any consideration to OOP practices):
internal class Parameter
{
public string Id;
public object Value;
}
Now, this populates the data table:
cmd.CommandText = "select * from Q25682067 where ID = #offerID and IsActive = #isActive";
this does not:
cmd.CommandText = "select * from Q25682067_Offers where ID = #offerID and IsActive = #isActive";
Using the SqlParameter constructor like that binds your parameters to SqlDbType.Int and SqlDbType.Bit instead of SqlDbType.Variant, which seems to be your weapon of choice. That's why your code works with the first table definition, and not the second.
I iterate over an external source and get a list of strings. I then insert them into the DB using:
SqlCommand command = new SqlCommand(commandString, connection);
command.ExecuteNonQuery();
Where commandString is an insert into command. i.e.
insert into MyTable values (1, "Frog")
Sometimes the string contains ' or " or \ and the insert fails.
Is there an elegant way to solve this (i.e. #"" or similar)?
Parameters.
insert into MyTable values (#id, #name)
And
int id = 1;
string name = "Fred";
SqlCommand command = new SqlCommand(commandString, connection);
command.Parameters.AddWithValue("id", id);
command.Parameters.AddWithValue("name", name);
command.ExecuteNonQuery();
Now name can have any number of quotes and it'll work fine. More importantly it is now safe from sql injection.
Tools like "dapper" (freely available on NuGet) make this easier:
int id = 1;
string name = "Fred";
connection.Execute("insert into MyTable values (#id, #name)",
new { id, name });
You should look into using parameterized queries. This will allow you insert the data no matter the content and also help you avoid possible future SQL injection.
http://csharp-station.com/Tutorial/AdoDotNet/Lesson06
http://www.c-sharpcorner.com/uploadfile/puranindia/parameterized-query-and-sql-injection-attacks/