I want to reuse a parameterized query in a loop.
(This query is a simple example, I don't think I could make the loop inside sql and just return the needed rows)
Instead of
private String sql = "SELECT v FROM t WHERE VAL_1 = #param_1";
for (int n=1;n<10;n++)
{
MySqlCommand m = new MySqlCommand(sql);
m.Parameters.AddWithValue("#param_1", n);
res = Convert.ToInt32(m.ExecuteScalar());
( ... )
}
I'd like to move the setup of the query outside the loop; something like
private String sql = "SELECT v FROM t WHERE VAL_1 = #param_1";
MySqlCommand m = new MySqlCommand(sql);
m.Parameters.Add("#param_1"); // does not exist
for (int n=1;n<10;n++)
{
m.Parameters.Set("#param_1", n); // does not exist
res = Convert.ToInt32(m.ExecuteScalar());
( ... )
}
So the server does not have to parse the same sql for each ilteration in loop.
Is that possible?
You can add a parameter with
m.Parameters.Add("#param_1", MySqlDbType.Int32);
and later in the loop assign a value with
m.Parameters["#param_1"].Value = n;
If you just need to run query for list of parms without do diffrent things on each result, You can create a string with a loop like that:
String where_str= VAL_1 = #param_1" OR VAL_1 = #param_2" OR VAL_1 = #param_3"...
String sql = "SELECT v FROM t WHERE " + where_str;
and then exec the query it will give the same result.
If you need to saparate results so you can make it with prepaerd statement. Also, I recommend you to read about stored procedure it may be the best soultion for you in some cases.
example for prepaerd statement: (more info in the link)
private static void SqlCommandPrepareEx(string connectionString)
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
SqlCommand command = new SqlCommand(null, connection);
// Create and prepare an SQL statement.
command.CommandText =
"INSERT INTO Region (RegionID, RegionDescription) " +
"VALUES (#id, #desc)";
SqlParameter idParam = new SqlParameter("#id", SqlDbType.Int, 0);
SqlParameter descParam =
new SqlParameter("#desc", SqlDbType.Text, 100);
idParam.Value = 20;
descParam.Value = "First Region";
command.Parameters.Add(idParam);
command.Parameters.Add(descParam);
// Call Prepare after setting the Commandtext and Parameters.
command.Prepare();
command.ExecuteNonQuery();
// Change parameter values and call ExecuteNonQuery.
command.Parameters[0].Value = 21;
command.Parameters[1].Value = "Second Region";
command.ExecuteNonQuery();
}
}
Yes, this should be possible! Have a look for SQL Prepared Statements!
You can just use:
cmd = new MySqlCommand("SELECT * FROM yourTable WHERE condition=#val1", MySqlConn.conn);
In the loop add the parameters and prepare the command
cmd.Parameters.AddWithValue("#val1", value);
cmd.Prepare();
after the loop execute your query with
cmd.ExecuteNonQuery();
Yep, you can do all of those things but unless that's just an example you'd want to use IN with all the values or a join to a bulk loaded temp table if there are a large number of them. The reason is that each round trip to the DB has a significant overhead that you can reduce from n to 1 with either of those techniques.
Related
I have the code below (I've included what I believe are all relevant sections):
private String readCommand = "SELECT LEVEL FROM USERS WHERE VAL_1 = ? AND VAL_# = ?;";
public bool read(string id)
{
level = -1;
MySqlCommand m = new MySqlCommand(readCommand);
m.Parameters.Add(new MySqlParameter("", val1));
m.Parameters.Add(new MySqlParameter("", val2));
MySqlDataReader r = m.ExecuteReader();
if (r.HasRows)
level = Convert.ToInt32(r.GetValue(0).ToString());
r.Close();
return true;
}
When I run this, I get an IndexOutOfBoundsException on adding the first parameter. What have I done wrong?
Try this instead:
private String readCommand =
"SELECT LEVEL FROM USERS WHERE VAL_1 = #param_val_1 AND VAL_2 = #param_val_2;";
public bool read(string id)
{
level = -1;
MySqlCommand m = new MySqlCommand(readCommand);
m.Parameters.AddWithValue("#param_val_1", val1);
m.Parameters.AddWithValue("#param_val_2", val2);
level = Convert.ToInt32(m.ExecuteScalar());
return true;
}
protected void Login1_Authenticate(object sender, AuthenticateEventArgs e)
{
MySqlConnection con = new MySqlConnection("server=localhost;User Id=root;database=result;password=1234");
con.Open();
MySqlCommand cmd = new MySqlCommand("Select * from users where username=?username and password=?password", con);
cmd.Parameters.Add(new MySqlParameter("username", this.Login1.UserName));
cmd.Parameters.Add(new MySqlParameter("password", this.Login1.Password));
MySqlDataReader dr = cmd.ExecuteReader();
if (dr.HasRows ==true)
{
e.Authenticated = true;
}
}
You need to use named parameters in your query. E.g.:
String readCommand = "SELECT LEVEL FROM USERS WHERE VAL_1 = ?param1 AND VAL_2 = ?param2";
Then, pass the parameter names when you instantiate your MySqlParameter objects like so:
m.Parameters.Add(new MySqlParameter("param1", val1));
m.Parameters.AddWithValue("parameter",value)
will be better option for parametrized query.
I don't think the MySql.Data classes support unnamed parameters. If you're keen to use them, you could access your MySql db via the Odbc drivers, they support this.
You'll need to name the parameters in your query:
"SELECT LEVEL FROM USERS WHERE VAL_1 = #val1 AND VAL_2 = #val2;"
I've chosen the param indicator "#", but recent versions of MySql.Data support both "#" and "?".
Then update your param constructor to pass in the correct param name (you don't need to include the param indicator here, although it doesn't make any difference if you do).
m.Parameters.Add(new MySqlParameter("val1", val1));
PS. You prob know this already, or it was just omitted in the snippet, but I think you forgot to call Read on your instance of ExecuteReader.
If you want to execute the sql many times, then you should use this way:
conn.Open();
cmd.Connection = conn;
cmd.CommandText = "INSERT INTO myTable VALUES(NULL, #number, #text)";
cmd.Prepare();
cmd.Parameters.AddWithValue("#number", 1);
cmd.Parameters.AddWithValue("#text", "One");
for (int i=1; i <= 1000; i++)
{
cmd.Parameters["#number"].Value = i;
cmd.Parameters["#text"].Value = "A string value";
cmd.ExecuteNonQuery();
}
First time is without "ExecuteNonQuery" just adding the parameters with faked values, then inside the loop you add the real values.
See this link:
https://dev.mysql.com/doc/connector-net/en/connector-net-programming-prepared-preparing.html
I want to get the value to insert a table in C#,something like this:
begin
insert into bk_library(floor,section) values('foo2','bar')
returning id into :outid;
select *from bk_library where id=:outid;
end;
Unfortunately, I failed
error info: Kiss.Linq.Linq2Sql.Test.EntryPoint.TestInsertReturnId:
Oracle.DataAccess.Client.OracleException : ORA-06550: line 3, column
1: PLS-00428: an INTO clause is expected in this SELECT statement
[Test]
public void TestInsertReturnId()
{
int ret = 0;
string connstring = "Data Source=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=localhost)(PORT=1521)))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=pdborcl)));User Id=system;Password=****;";
string sql = #"insert into bk_library(floor,section) values('foo','bar') returning id into :outid";
sql = getSqlString();
using (DbConnection conn = new OracleConnection(connstring))
{
conn.Open();
DbCommand command = conn.CreateCommand();
command.CommandType = CommandType.Text;
command.CommandText = sql;
OracleParameter lastId = new OracleParameter(":outid", OracleDbType.Int32);
lastId.Direction = ParameterDirection.Output;
command.Parameters.Add(lastId);
ret = command.ExecuteNonQuery();
// this code work fine ,now I want to get the entire record
LogManager.GetLogger<EntryPoint>().Info("The new id ={0}", lastId.Value.ToString());
conn.Close();
}
Assert.AreNotEqual(ret, 0);
}
ParameterDirection should be ReturnValue
lastId.Direction = ParameterDirection.ReturnValue;
From < http://arjudba.blogspot.ch/2008/07/pls-00428-into-clause-is-expected-in.html?m=1>
You need to write SELECT * INTO some_variable FROM bk_library instead of SELECT * FROM bk_library because I assume you want to store the data retrieved somehow. Therefore you need to declare a new variable some_variable (I assume of type string) and modify your SELECT statement as above. The data from the statement will then be stored in your new variable.
Hope this helps
When I try to run this, it gives me the following error message:
Conversion failed when converting the varchar value 'category_id' to data type int.
Here's my SQL and parameter code, I supposed it should work, but it doesn't.
mycmd.CommandText="SELECT * FROM categories WHERE #db_property = #property_id";
// This contains a string "category_id", which is correct.
mycmd.Parameters.Add("#db_property", SqlDbType.VarChar).Value=db_property_field;
// This contains an Int, referring to the category_id in database. As of now, this is 1
mycmd.Parameters.Add("#property_id", SqlDbType.Int).Value=property_id;
After I'm going through this code, I run it through a Reader, and that's where I get the error message above. Been asking teacher, and excellent students in my class, no one can find a clue on, where the problem is.
You shouldn't add field name as parameter. Try to change your script to include actual field id:
mycmd.CommandText = "SELECT * FROM categories WHERE category_id = #property_id";
mycmd.Parameters.Add("#property_id", SqlDbType.Int).Value = property_id;
I'm not sure about your structure, but try the following:
mycmd.CommandText = "SELECT * FROM categories WHERE Cast(#db_property as Int) = #property_id";
Your query is matching the two variables you are passing in so it will either return all the data or none of it! On top of that you are matching a char variable with an int. SQL will try to cast the char variable to an int.
#db_property = #property_id
should your query look like this?
SELECT * FROM categories WHERE db_property = #db_property AND property_id = #property_id
If you look at your statement you are comparing the two parameters. The WHERE clause is not on a table column ("categories") and the two parameters you are passing are different data types. VarChar and Int. When that command is executed the SQL engine is trying to compare two variables of different data types.
If you run the following SQL statements straight against SQL you will receive the same error.
DECLARE #Var1 VARCHAR(100)
DECLARE #Var2 INT
SELECT #Var1 = 'Test', #Var2 = 1
SELECT * FROM dbo.categories WHERE #Var1 = #Var2
You can get solution from the following address:
http://net-informations.com/csprj/data-providers/cs-procedure-parameter.htm
For your information I Just reshape the code and use it to my needs.
Code of Stored Procedure is as follow:
Create PROCEDURE [dbo].[PmSPValidate]
#a varchar(10)
AS
BEGIN
(SELECT AcctDsc,AcctAge
FROM dbo.tblCoa
WHERE AcctNo >= #a)
END
Code of C# :
private void btnThirdTrial_Click(object sender, EventArgs e)
{
string connetionString = null;
SqlConnection connection;
SqlDataAdapter adapter;
SqlCommand command = new SqlCommand();
SqlParameter param;
DataSet ds = new DataSet();
int i = 0;
connetionString = "Data Source=FIN03;Initial Catalog=CmsTest;Integrated Security=True";
connection = new SqlConnection(connetionString);
connection.Open();
command.Connection = connection;
command.CommandType = CommandType.StoredProcedure;
command.CommandText = "dbo.PmSPValidate";
param = new SqlParameter("#a",Account.Text.ToString ());
param.Direction = ParameterDirection.Input;
param.DbType = DbType.String;
command.Parameters.Add(param);
adapter = new SqlDataAdapter(command);
adapter.Fill(ds);
for (i = 0; i <= ds.Tables[0].Rows.Count - 1; i++)
{
MessageBox.Show(" Name " + ds.Tables[0].Rows[i][0].ToString() + " Age " + ds.Tables[0].Rows[i][1].ToString());
}
connection.Close();
}
For the past few hours I am trying to do the simplest of the simple things (at least for SQL SERVER) in an Oracle Data Base, through a .NET application using ADO.NET. It seems impossible.
For SQL SERVER I would do this simple task, supposing I have an SqlCommand object
comm.CommandText = #"
DECLARE #next_id INT
SET #next_id = (SELECT ISNULL(MAX(id_col),0) FROM TABLE_1) + 1
INSERT INTO TABLE_1 (id_col, col1, ...) VALUES (#next_id, val1, ...)
SELECT #next_id";
int id = Convert.ToInt32(comm.ExecuteScalar());
That would insert a new record to table TABLE_1 and I would take back the new id in the "id" variable in c# code.
Four simple steps
Declare a variable
Set it to the next available id
Insert the record with the new variable
Return the variable's value
Ok I managed to declare the variable in Oracle query. Also I (think) I managed to give it a value (With SELECT INTO)
How can I get back this variable's value back in c#? How can i SELECT a variable's value to the output stream in Oracle SQL?
I know that there are better ways to achieve getting back an identity column, but that's not the question here. It could be a totally different example. The question is simple.: I have declared a variable inside an oracle sql script that will be executed from within .net app. How can i get the variable's value back to c#, from an oracle query? What is the above code's equivalent with Oracle ADO.NET query?
You'll want to use ODP.NET (Oracle's Oracle Data Access Components):
An example of this is below. Note that in ODP.NET, you can establish a parameters direction (input, inputoutput, output, returnvalue) to correspond with the parameters of the procedure or statement you're running. In this example, I'm grabbing a returnvalue, which is an ID that is generated by the db via a sequence and trigger (its created automagically as far as the .NET app is concerned):
int event_id = 0;
using (OracleConnection oraConn = new OracleConnection(connStr))
{
string cmdText = #"insert into EVENT
(EVENT_NAME, EVENT_DESC)
values
(:EVENT_NAME, :EVENT_DESC)
RETURNING EVENT_ID INTO :EVENT_ID
";
using (OracleCommand cmd = new OracleCommand(cmdText, oraConn))
{
oraConn.Open();
OracleTransaction trans = oraConn.BeginTransaction();
try
{
OracleParameter prm = new OracleParameter();
cmd.BindByName = true;
prm = new OracleParameter("EVENT_NAME", OracleDbType.Varchar2);
prm.Value = "SOME NAME"; cmd.Parameters.Add(prm);
prm = new OracleParameter("EVENT_DESC", OracleDbType.Varchar2);
prm.Value = "SOME DESC"; cmd.Parameters.Add(prm);
prm = new OracleParameter( "EVENT_ID"
, OracleDbType.Int32
, ParameterDirection.ReturnValue);
cmd.Parameters.Add(prm);
cmd.ExecuteNonQuery();
trans.Commit();
// return value
event_id = ConvertFromDB<int>(cmd.Parameters["EVENT_ID"].Value);
}
catch
{
trans.Rollback();
throw;
}
finally
{
trans.Dispose();
}
oraConn.Close();
}
}
The ConvertFromDB is just a generic to cast the return value to its .NET equivalent (an int in this case).
Hope that helps.
EDIT:
You can easily bind an array of values (and retrieve an array of return values) in ODP.NET:
using (OracleConnection oraConn = new OracleConnection(connStr))
{
string cmdText = #"insert into TEST_EVENT
(EVENT_NAME, EVENT_DESC)
values
(:EVENT_NAME, :EVENT_DESC)
RETURNING EVENT_ID INTO :EVENT_ID
";
using (OracleCommand cmd = new OracleCommand(cmdText, oraConn))
{
oraConn.Open();
OracleTransaction trans = oraConn.BeginTransaction();
try
{
string[] event_names = new string[2];
string[] event_descs = new string[2];
int[] event_ids = new int[2];
event_names[0] = "Event1";
event_descs[0] = "Desc1";
event_names[1] = "Event2";
event_descs[1] = "Desc2";
OracleParameter prm = new OracleParameter();
cmd.Parameters.Clear();
cmd.ArrayBindCount = 2;
cmd.BindByName = true;
prm = new OracleParameter("EVENT_NAME", OracleDbType.Varchar2);
prm.Value = event_names; cmd.Parameters.Add(prm);
prm = new OracleParameter("EVENT_DESC", OracleDbType.Varchar2);
prm.Value = event_descs; cmd.Parameters.Add(prm);
prm = new OracleParameter( "EVENT_ID"
, OracleDbType.Int32
, ParameterDirection.ReturnValue);
cmd.Parameters.Add(prm);
cmd.ExecuteNonQuery();
trans.Commit();
// get return values
event_ids = (int[])(cmd.Parameters["EVENT_ID"].Value);
}
catch
{
trans.Rollback();
throw;
}
finally
{
trans.Dispose();
}
oraConn.Close();
}
}
I have the code below (I've included what I believe are all relevant sections):
private String readCommand = "SELECT LEVEL FROM USERS WHERE VAL_1 = ? AND VAL_# = ?;";
public bool read(string id)
{
level = -1;
MySqlCommand m = new MySqlCommand(readCommand);
m.Parameters.Add(new MySqlParameter("", val1));
m.Parameters.Add(new MySqlParameter("", val2));
MySqlDataReader r = m.ExecuteReader();
if (r.HasRows)
level = Convert.ToInt32(r.GetValue(0).ToString());
r.Close();
return true;
}
When I run this, I get an IndexOutOfBoundsException on adding the first parameter. What have I done wrong?
Try this instead:
private String readCommand =
"SELECT LEVEL FROM USERS WHERE VAL_1 = #param_val_1 AND VAL_2 = #param_val_2;";
public bool read(string id)
{
level = -1;
MySqlCommand m = new MySqlCommand(readCommand);
m.Parameters.AddWithValue("#param_val_1", val1);
m.Parameters.AddWithValue("#param_val_2", val2);
level = Convert.ToInt32(m.ExecuteScalar());
return true;
}
protected void Login1_Authenticate(object sender, AuthenticateEventArgs e)
{
MySqlConnection con = new MySqlConnection("server=localhost;User Id=root;database=result;password=1234");
con.Open();
MySqlCommand cmd = new MySqlCommand("Select * from users where username=?username and password=?password", con);
cmd.Parameters.Add(new MySqlParameter("username", this.Login1.UserName));
cmd.Parameters.Add(new MySqlParameter("password", this.Login1.Password));
MySqlDataReader dr = cmd.ExecuteReader();
if (dr.HasRows ==true)
{
e.Authenticated = true;
}
}
You need to use named parameters in your query. E.g.:
String readCommand = "SELECT LEVEL FROM USERS WHERE VAL_1 = ?param1 AND VAL_2 = ?param2";
Then, pass the parameter names when you instantiate your MySqlParameter objects like so:
m.Parameters.Add(new MySqlParameter("param1", val1));
m.Parameters.AddWithValue("parameter",value)
will be better option for parametrized query.
I don't think the MySql.Data classes support unnamed parameters. If you're keen to use them, you could access your MySql db via the Odbc drivers, they support this.
You'll need to name the parameters in your query:
"SELECT LEVEL FROM USERS WHERE VAL_1 = #val1 AND VAL_2 = #val2;"
I've chosen the param indicator "#", but recent versions of MySql.Data support both "#" and "?".
Then update your param constructor to pass in the correct param name (you don't need to include the param indicator here, although it doesn't make any difference if you do).
m.Parameters.Add(new MySqlParameter("val1", val1));
PS. You prob know this already, or it was just omitted in the snippet, but I think you forgot to call Read on your instance of ExecuteReader.
If you want to execute the sql many times, then you should use this way:
conn.Open();
cmd.Connection = conn;
cmd.CommandText = "INSERT INTO myTable VALUES(NULL, #number, #text)";
cmd.Prepare();
cmd.Parameters.AddWithValue("#number", 1);
cmd.Parameters.AddWithValue("#text", "One");
for (int i=1; i <= 1000; i++)
{
cmd.Parameters["#number"].Value = i;
cmd.Parameters["#text"].Value = "A string value";
cmd.ExecuteNonQuery();
}
First time is without "ExecuteNonQuery" just adding the parameters with faked values, then inside the loop you add the real values.
See this link:
https://dev.mysql.com/doc/connector-net/en/connector-net-programming-prepared-preparing.html