I am trying to perform dynamic sql select where I am selecting from a table using a parameter.
SELECT null FROM #TableName
However I am getting error must declare table variable #TableName. I suspect this is because I am selecting from a table using a variable. I have not needed to do this before.
List<SqlParameter> sqlParams = new List<SqlParameter>()
{
new SqlParameter("TableName", "testtable"),
new SqlParameter("FieldName", "testfield"),
new SqlParameter("Find", "testfind"),
};
string sqlSelect = "SELECT null FROM #TableName
WHERE #FieldName LIKE '%' + #Find + '%' ";
DataTable dtSelect = SqlHelper.ExecuteDataset(sqlConn, CommandType.Text,
sqlSelect, 30, sqlParams.ToArray()).Tables[0];
//30 = timeout
How can I perform the above using dynamic sql? (no stored procedures please)
You cannot use parameters for things like table and column names. For those you could have a whitelist of possible values and then use string concatenation when building the SQL query.
You can't use parameters like that, so you have to build the query as a string. You could do that in SQL, but you can also just create the string in the C# code.
Make sure that the table name and field name are safe and trusted values, and doesn't come directly from an unsafe source like a web request.
string tableName = "testtable";
string fieldName = "testfield";
List<SqlParameter> sqlParams = new List<SqlParameter>() {
new SqlParameter("Find", "testfind"),
};
string sqlSelect =
"SELECT null " +
"FROM " + tableName + " " +
"WHERE " + fieldName + " LIKE '%' + #Find + '%' ";
private DataTable ExecuteDynamic(string TableName,string FieldName, string Find)
{
string sqlSelect = "SELECT * FROM " + TableName +
" WHERE " + FieldName + " LIKE '%'" + Find + "'%' ";
using (connection = new SqlConnection(Strcon))
connection.Open();
{
using (cmd = new SqlCommand(sqlSelect, connection))
{
cmd.CommandType = CommandType.Text;
cmd.CommandTimeout = 60;
adpt = new SqlDataAdapter(cmd);
dt = new DataTable();
adpt.Fill(dt);
return (dt);
}
}
}
Related
I'm trying to write an sql query in C# to Access.
the query works fine in the Access software but when I tried to run it through C# with parameters it throws me exception that the parameters are missing. it's like it ignores the parameters I'm giving.
this is my method:
public DataTable GetRoomStatusByDate(Room RoomItem, TimeRange TimeItem, WeekDay DayItem, DateTime DateItem)
{
string cmdStr = "SELECT Subject, AdditionOrCancellation, Description " +
"FROM (SELECT * " +
"FROM TimeTables TT1 LEFT OUTER JOIN SpecialEvents SE1 " +
"ON TT1.RoomNumber = SE1.RoomNumber AND TT1.StructureNumber = SE1.StructureNumber AND " +
"TT1.DayNumber = SE1.DayNumber AND TT1.HourNumber = SE1.HourNumber " +
"UNION " +
"SELECT * " +
"FROM TimeTables TT2 RIGHT OUTER JOIN SpecialEvents SE2 " +
"ON TT2.RoomNumber = SE2.RoomNumber AND TT2.StructureNumber = SE2.StructureNumber " +
"AND TT2.DayNumber = SE2.DayNumber AND TT2.HourNumber = SE2.HourNumber) " +
"WHERE (TT1.RoomNumber = #TTRoomNumber OR SE1.RoomNumber = #SERoomNumber) AND (TT1.StructureNumber = #TTStructureNumber OR SE1.StructureNumber = #SEStructureNumber) " +
"AND(TT1.HourNumber = #TTHourNumber OR SE1.HourNumber = #SEHourNumber) AND (TT1.DayNumber = #TTDayNumber OR SE1.DayNumber = ##SEDayNumber) " +
"AND (SE1.EventDate = #SEEventDate OR SE1.EventDate IS NULL)";
DataSet ds = new DataSet();
using (OleDbCommand command = new OleDbCommand(cmdStr))
{
command.Parameters.AddWithValue("#TTRoomNumber", RoomItem.Number);
command.Parameters.AddWithValue("#SERoomNumber", RoomItem.Number);
command.Parameters.AddWithValue("#TTStructureNumber", RoomItem.Structure);
command.Parameters.AddWithValue("#SEStructureNumber", RoomItem.Structure);
command.Parameters.AddWithValue("#TTDayNumber", TimeItem.Number);
command.Parameters.AddWithValue("#SEDayNumber", TimeItem.Number);
command.Parameters.AddWithValue("#TTDayNumber", DayItem.Number);
command.Parameters.AddWithValue("#SEDayNumber", DayItem.Number);
command.Parameters.AddWithValue("#SEEventDate", DateItem);
ds = GetMultipleQuery(command);
}
DataTable dt = new DataTable();
try
{
dt = ds.Tables[0];
}
catch
{ }
return dt;
}
I also want to mention I use OLEDB and my connection is surly fine because I have many methods with query and parameters and their all works fine with no problems.
this is my only method with a query that doesn't work.
OleDb uses positional parameters, not named parameters. Replace the parameter names by ? in SQL. You can leave the parameter name unchanged in AddWithValue, since it is ignored.
...
"WHERE (TT1.RoomNumber = ? OR SE1.RoomNumber = ?) AND (TT1.StructureNumber = ? OR SE1.StructureNumber = ?) " +
"AND(TT1.HourNumber = ? OR SE1.HourNumber = ?) AND (TT1.DayNumber = ? OR SE1.DayNumber = ?) " +
"AND (SE1.EventDate = ? OR SE1.EventDate IS NULL)";
Make sure you add the parameters in the same order they appear in the SQL text.
Also the value passed to AddWithValue must have the right type. A numeric or date string is not automatically converted to the appropriate numeric or DateTime type.
I'm having trouble with this method. It returns empty string, what is wrong with this ?
I have this method:
public static string GetData(string Table1, string Column1, string WhereColumn, string WhereValue)
{
Table1 = Methods.cleaninjection(Table1); // Some injection method that cleans the string
SqlConnection connection = new SqlConnection(WebConfigurationManager.ConnectionStrings["connection"].ConnectionString);
SqlCommand command = new SqlCommand("SELECT " + "#Column1" + " FROM " + Table1 + " WHERE " + "#WhereColumn" + " = " + "#WhereValue", connection);
command.Parameters.AddWithValue("Column1", Column1);
command.Parameters.AddWithValue("WhereColumn", WhereColumn);
command.Parameters.AddWithValue("WhereValue", WhereValue);
try
{
if ((connection.State == ConnectionState.Closed) || (connection.State == ConnectionState.Broken))
{
connection.Open();
}
string veri = Convert.ToString(command.ExecuteScalar());
return veri;
}
finally
{
connection.Close();
}
}
When I run this, the command string looks like this:
SELECT #Column1 FROM Table1 WHERE #WhereColumn = #WhereValue
It looks like correct but I couldn't find what is wrong.
Any ideas?
As commented, you cannot parameterize your column names and table names. Instead, do string concatenation:
"SELECT " + Column1 + " FROM " + Table1 + " WHERE " + WhereColumn + " = #WhereValue";
Here is how your code should be:
public static string GetData(string Table1, string Column1, string WhereColumn, string WhereValue)
{
Table1 = Methods.cleaninjection(Table1); // My injection method that cleans the string
string sql = "SELECT " + Column1 + " FROM " + Table1 + " WHERE " + #WhereColumn + " = #WhereValue";
using (SqlConnection connection = new SqlConnection(WebConfigurationManager.ConnectionStrings["connection"].ConnectionString))
{
using (SqlCommand command = new SqlCommand(sql, connection))
{
command.Parameters.Add("#WhereValue", SqlDbType.VarChar, 50).Value = WhereValue;
connection.Open();
string veri = Convert.ToString(command.ExecuteScalar());
return veri;
}
}
}
Notes:
Please do not use AddWithValue. Use Parameters.Add() instead. According to this article:
There is a problem with the AddWithValue() function: it has to infer
the database type for your query parameter. Here’s the thing:
sometimes it gets it wrong.
Wrap your object in Using to ensure proper cleanup of resources.
For additional security purposes, you can wrap your column name and table name in square brackets [].
string connetionString = null;
SqlConnection connection;
SqlCommand command;
SqlDataAdapter adpter = new SqlDataAdapter();
DataSet ds = new DataSet();
XmlReader xmlFile;
string sql = null;
int ID = 0;
string Name = null, Text = null, Screenname = null;
connetionString = "myconnection";
connection = new SqlConnection(connetionString);
xmlFile = XmlReader.Create("my.XML", new XmlReaderSettings());
ds.ReadXml(xmlFile);
int i = 0;
connection.Open();
for (i = 0; i <= ds.Tables[0].Rows.Count - 1; i++)
{
ID = Convert.ToInt32(ds.Tables[0].Rows[i].ItemArray[0]);
Text = ds.Tables[0].Rows[i].ItemArray[1].ToString().Replace("'", "''");
Name = ds.Tables[0].Rows[i].ItemArray[2].ToString().Replace("'", "''");
Screenname = ds.Tables[0].Rows[i].ItemArray[3].ToString().Replace("'", "''");
//sql = "insert into nicktest values(" + ID + ",'" + Text + "'," + Name + "," + Screenname + "," + DateTime.Now.ToString() + ")";
sql = "If Exists(Select * from niktest2 Where Id = ID) " +
" BEGIN " +
" update niktest2 set Name = '" + Text + "' , Screenname = '" + Name + "', Profimg= '" + Screenname + "', InsertDateTime= '" + DateTime.Now.ToString() + "' where Id=ID" +
" END " +
" ELSE " +
" BEGIN " +
" insert into niktest2(Id,Name,Screenname,Profimg,InsertDateTime) values('" + ID + "','" + Text + "','" + Name + "','" + Screenname + "' ,'" + DateTime.Now.ToString() + "')" +
" END ";
command = new SqlCommand(sql, connection);
adpter.InsertCommand = command;
adpter.InsertCommand.ExecuteNonQuery();
}
}
after running this code only first row gets updated even my xml file is having more data.
i Want to insert all data into database with assign id to it in xml file.
Please help..
As soon as you have inserted one row, this condition will be true:
If Exists(Select * from niktest2 Where Id = ID)
So you will perform the update, rather than the insert, so you will only ever get one row in the database.
Since you are using SQL Server 2008 I would adopt a completely different approach, using Parameterised queries, MERGE, and table valued parameters.
The first step would be to create your table valued parameter (I have had to guess at your type:
CREATE TYPE dbo.nicktestTableType AS TABLE
(
Id INT,
Name VARCHAR(255),
Screenname VARCHAR(255),
Profimg VARCHAR(255)
);
Then you can write your MERGE statement to upsert to the database:
MERGE nicktest WITH (HOLDLOCK) AS t
USING #NickTestType AS s
ON s.ID = t.ID
WHEN MATCHED THEN
UPDATE
SET Name = s.Name,
Screenname = s.Screenname,
Profimg = s.Profimg,
InsertDateTime = GETDATE()
WHEN NOT MATCHED THEN
INSERT (Id, Name, Screenname, Profimg, InsertDateTime)
VALUES (s.Id, s.Name, s.Screenname, s.Profimg, GETDATE());
Then you can pass your datatable to the query as a parameter:
using (var command = new SqlCommand(sql, connection))
{
var parameter = new SqlParameter("#NickTestType", SqlDbType.Structured);
parameter.Value = ds.Tables[0];
parameter.TypeName = "dbo.nicktestTableType";
command.Parameters.Add(parameter);
command.ExecuteNonQuery();
}
If you don't want to make such a drastic change, then you should at the very least use parameterised queries, so your SQL would be:
IF EXISTS (SELECT 1 FROM nicktest WHERE ID = #ID)
BEGIN
UPDATE nicktest
SET Name = #Name,
ScreenName = #ScreeName,
InsertDateTime = GETDATE()
WHERE ID = #ID;
END
ELSE
BEGIN
INSERT (Id, Name, Screenname, Profimg, InsertDateTime)
VALUES (#ID, #Name, #Screenname, #Profimg, GETDATE());
END
Or preferably still using MERGE as the HOLDLOCK table hint prevents (or at least massively reduces the chance of) a race condition:
MERGE nicktest WITH (HOLDLOCK) AS t
USING (VALUES (#ID, #Name, #ScreenName, #ProfImg)) AS s (ID, Name, ScreenName, ProfImg)
ON s.ID = t.ID
WHEN MATCHED THEN
UPDATE
SET Name = s.Name,
Screenname = s.Screenname,
Profimg = s.Profimg,
InsertDateTime = GETDATE()
WHEN NOT MATCHED THEN
INSERT (Id, Name, Screenname, Profimg, InsertDateTime)
VALUES (s.Id, s.Name, s.Screenname, s.Profimg, GETDATE());
This will be considerably less efficient than the first solution though using table-valued parameter
Then your c# would be something like:
for (i = 0; i <= ds.Tables[0].Rows.Count - 1; i++)
{
using (var command = new SqlCommand(sql, connection))
{
command.Parameters.AddWithValue("#ID", ds.Tables[0].Rows[i][0]);
command.Parameters.AddWithValue("#Name", ds.Tables[0].Rows[i][1]);
command.Parameters.AddWithValue("#ScreeName", ds.Tables[0].Rows[i][2]);
command.Parameters.AddWithValue("#ProfImg", ds.Tables[0].Rows[i][3]);
command.ExecuteNonQuery();
}
}
I build a Web Service in ASP.Net which sends me a list of rooms.
The parameters are id's which are separated by a comma.
I saved them to a string and build a sql select query.
When I send all 4 parameters I everything works fine and I get a result. But when I send less then 4 I get an error.
System.Data.SqlClient.SqlException: Incorrect syntax near ')'.
How can I set my the parameters optional in the sql query to select just the values I entered?
Here is my code so far:
internal static List<RAUM> Raum(string RAUMKLASSE_ID, string STADT_ID, string GEBAEUDE_ID, string REGION_ID)
{
List<RAUM> strasseObject = new List<RAUM>();
string raumklasseid = RAUMKLASSE_ID;
string gebaudeid = GEBAEUDE_ID;
string stadtid = STADT_ID;
string regionid = REGION_ID;
using (SqlConnection con = new SqlConnection(#"Data Source=Localhost\SQLEXPRESS;Initial Catalog=BOOK-IT-V2;Integrated Security=true;"))
using (SqlCommand cmd = new SqlCommand(#"SELECT r.BEZEICHNUNG AS BEZEICHNUNG, r.ID AS ID FROM RAUM r WHERE RAUMKLASSE_ID IN (" + raumklasseid + ") AND STADT_ID IN (" + stadtid + ") AND GEBAEUDE_ID IN (" + gebaudeid + ") AND REGION_ID IN (" + regionid + ")", con))
{
con.Open();
using (SqlDataReader rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
if (rdr["BEZEICHNUNG"] != DBNull.Value && rdr["ID"] != DBNull.Value)
{
strasseObject.Add(new RAUM()
{
RaumName = rdr["BEZEICHNUNG"].ToString(),
RaumID = rdr["ID"].ToString()
});
}
}
}
}
return strasseObject;
}
Thanks in advance for your help.
Imagine the parameter REGION_ID is an empty string. That part of your query will be something like:
...AND REGION_ID IN ()...
Because in AND REGION_ID IN (" + regionid + ")" the regionid variable will be replaced with an empty string. This is not valid SQL syntax so you'll get that exception.
Declare a function like this:
private static void AppendConstrain(StringBuilder query, string name, string value)
{
if (String.IsNullOrWhiteSpace(value))
return;
if (query.Length > 0)
query.Append(" AND ");
query.AppendFormat("{0} IN ({1})", name, value);
}
Then change your code to build the query in this way:
StringBuilder constrains = new StringBuilder();
AppendConstrain(contrains, "RAUMKLASSE_ID", RAUMKLASSE_ID);
AppendConstrain(contrains, "GEBAEUDE_ID", GEBAEUDE_ID);
AppendConstrain(contrains, "STADT_ID", STADT_ID);
AppendConstrain(contrains, "REGION_ID", REGION_ID);
StringBuilder query =
new StringBuilder("SELECT r.BEZEICHNUNG AS BEZEICHNUNG, r.ID AS ID FROM RAUM r");
if (constrains.Length > 0)
{
query.Append(" WHERE ");
query.Append(constrains);
}
using (SqlCommand cmd = new SqlCommand(query.ToString(), con))
{
// Your code...
}
WARNING: DO NOT USE this code in production or when the input comes from the user because it's vulnerable to SQL injection. For better approaches (do not stop to the accepted answer) see Parameterize an SQL IN clause
It always be a better approach to write the stored procedures and pass the parameters. But in your approach you should split your query because of not sure the values. So, your code be something like that..
Test it yourself, i didnt check it
string raumklasseid = RAUMKLASSE_ID;
string gebaudeid = GEBAEUDE_ID;
string stadtid = STADT_ID;
string regionid = REGION_ID;
string whereClause = string.Empty;
if (!string.IsNullorEmpty(raumklasseid))
{
whereClause = "RAUMKLASSE_ID IN (" + raumklasseid + ")";
}
if (!string.IsNullorEmpty(stadtid ))
{
if(string.IsNullorEmpty(whereClause)
whereClause = "STADT_ID IN (" + stadtid + ")";
else
whereClause += "AND RSTADT_ID IN (" + stadtid + ")";
}
if (!string.IsNullorEmpty(stadtid ))
{
if(string.IsNullorEmpty(whereClause)
whereClause = "STADT_ID IN (" + stadtid + ")";
else
whereClause += "AND RSTADT_ID IN (" + stadtid + ")";
}
if (!string.IsNullorEmpty(regionid))
{
if(string.IsNullorEmpty(whereClause)
whereClause = "REGION_ID IN (" + regionid + ")";
else
whereClause += "AND REGION_ID IN (" + regionid + ")";
}
if(!string.IsNullorEmpty(whereClause)
whereClause = "WHERE " + whereClause ;
// now your cmd should be like that
using (SqlCommand cmd = new SqlCommand(#"SELECT r.BEZEICHNUNG AS BEZEICHNUNG, r.ID AS ID FROM RAUM r " + whereClause , con))
I'm not sure what the issue is but my code looks like:
function() {
string sqltext2;
sqltext2 = "INSERT into myTable";
SqlCommand myCommand2 = new SqlCommand(sqltext2, MyConnection2);
if (cond1) {
sqltext2 = sqltext2 + "SELECT" + "#initOwnerFirstName" + "," + "#ownerFirstName" + "UNION ALL ";
SqlParameter param = new SqlParameter();
param.ParameterName = "#initOwnerFirstName";
param.Value = initOwnerFirstName;
SqlParameter param2 = new SqlParameter();
param2.ParameterName = "#ownerFirstName";
param2.Value = owner.FirstName;
myCommand2.Parameters.Add(param);
myCommand2.Parameters.Add(param2);
I'm completely new with parameterized SQL but the syntax seems right to me. The error I keep getting is:
Must declare the scalar variable "#initOwnerFirstName".
The reason I'm writing the statement like that is because I intend to have multiple other if statements that will add to the SQLtext
EDIT: Here is the full part of the code after the if statement since my syntax makes little sense without the other variables. This is cleaned up after what JYelton suggested but I'm still getting the same error.
sqltext2 = sqltext2 + "SELECT" + "'" + currentUserId2 + "'," + "'" + owner.Id.ToString() + "'," + "'" + DateTime.Now + "'," + "'FirstName', #initOwnerFirstName, #ownerFirstName UNION ALL ";
myCommand2.Parameters.AddWithValue("initOwnerFirstName", initOwner.FirstName);
myCommand2.Parameters.AddWithValue("OwnerFirstName", owner.FirstName);
Problem could be that you are concatenating your strings without spaces. You don't need to concatenate either. See my update below:
function() {
string sqltext2;
sqltext2 = "INSERT into dbo.OwnerChanges ";
SqlCommand myCommand2 = new SqlCommand(sqltext2, MyConnection2);
if (cond1) {
//no concatenating
sqltext2 = sqltext2 + " SELECT #initOwnerFirstName , #ownerFirstName UNION ALL ";
SqlParameter param = new SqlParameter();
param.ParameterName = "#initOwnerFirstName";
param.Value = initOwnerFirstName;
SqlParameter param2 = new SqlParameter();
param2.ParameterName = "#ownerFirstName";
param2.Value = owner.FirstName;
myCommand2.Parameters.Add(param);
myCommand2.Parameters.Add(param2);
I'm not sure what the rest of your code does but I suspect you are leaving a trailing UNION ALL at the end of all that. You could probably benefit by just putting each sub query into an array and using String.Join on them.
UPDATE:
I think I see the issue. You need to update CommandText and not the original string. So change this:
sqltext2 = sqltext2 + " SELECT #initOwnerFirstName , #ownerFirstName UNION ALL ";
to this:
myCommand2.CommandText= sqltext2 + " SELECT #initOwnerFirstName , #ownerFirstName UNION ALL ";
Here's how parameterized queries work:
using (SqlConnection conn = new SqlConnection("connectionstring"))
{
using SqlCommand cmd = conn.CreateCommand())
{
cmd.CommandText = #"INSERT INTO mytable
(initOwnerFirstName, ownerFirstName)
VALUES (#initOwnerFirstName, #ownerFirstName);";
cmd.Parameters.AddWithValue("initOwnerFirstName", initOwner.FirstName);
cmd.Parameters.AddWithValue("ownerFirstName", owner.FirstName);
// ... execute query
}
}
It looks like you are inserting into myTable the results of a SELECT statement, but you are trying to pass parameters where the column names should go. The Parameter will replace the #variablename with the value, thus: Parameters are for the values, not the column names.
For multiple values, use parenthesis to specify the set, separated by commas:
INSERT INTO mytable (col1, col2) VALUES ("a", "b"), ("c", "d"), ("e", "f");
You can modify the query string appropriately to fit this syntax.
More information: SQL Insert Syntax