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
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 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.
I would like to create a simple login page in asp.net. here is my code:
protected void Button1_Click(object sender, EventArgs e)
{
SqlConnection conn = new SqlConnection();
conn.ConnectionString = "Data Source=TEST-PC\\SQLSERVER2012;Initial Catalog=oncf;Integrated Security=True";
conn.Open();
string query = "SELECT COUNT(*) FROM Account WHERE acc_username= '" + TextBox1.Text + "' AND acc_password= '" + TextBox2.Text + "'";
SqlCommand cmd = new SqlCommand(query, conn);
SqlDataReader myreader = cmd.ExecuteReader();
int count = 0;
while(myreader.Read())
{
count = count + 1;
}
if(count==1)
{
Response.Redirect("page2.aspx");
}
else
{
Label1.Visible = true;
}
conn.Close();
}
I set a counter in order to know if the credentials entered are present in the DB. If the value of the counter goes to 1, the login is successful. Otherwise, the label with a message error is displayed!
However, whatever I enter as input in the username and login textboxes, it always redirect me to the other page !
For now, my concern is not the security aspects, I just want to test this simple code, I don't see any problem with the code, but still it doesnt work, it is driving me crazy...
The reason that you are always redirecting is that your reader always returns 1 row, whether there is a match or not. If there is a match in your database, then the query will return
(no column name)
---------------
1
If there is not a match then it will return:
(no column name)
---------------
0
Either way, myreader.Read() will return true, and you will increment count in this part:
while(myreader.Read())
{
count = count + 1;
}
if(count==1)
{
Response.Redirect("page2.aspx");
}
Rather than checking the if the query returns rows you can retrieve the value of the count using SqlCommand.ExecuteScalar(). In addition to this I would make three more changes:
1. Use parameterised queries
This is not just a security concern, parameterised queries are able to use cached plans, whereas if you concatenate the parameters into the query then a new plan is compliled for each new variable value. In addition, parameterised queries are more strongly typed, and you don't need to escape things like O'shea to ensure that your extra quote doesn't mess up the query.
2. Encrypt the passwords
This is directly to do with security so should really be overlooked as per your request to not comment on security, HOWEVER, this answer is not just for your benefit, and a half answer is likely to be read by someone in the future who may or may not be aware of the risks of storing plain text passwords. There is a simple encryption method in this answer.
3. Add using blocks to your code
A minor change, but when you have objects that implement IDisposable it is a good idea to use a using block to esnure they are disposed of properly.
So you might end up with:
string password = SomeStaticClass.Encrypt(TextBox2.Text);
string connectionString = "Data Source=TEST-PC\\SQLSERVER2012;Initial Catalog=oncf;Integrated Security=True";
string query = "SELECT UserCount = COUNT(*) FROM Account WHERE acc_username= #UserName AND acc_password= #Password";
using (var connection = new SqlConnection(connectionString))
using (var command = new SqlCommand(query, connection))
{
connection.Open();
command.Parameters.Add("#UserName", SqlDbType.VarChar, 50).Value = TextBox1.Text;
command.Parameters.Add("#Password", SqlDbType.VarChar, 50).Value = password;
int count = Convert.ToInt32(command.ExecuteScalar());
if(count==1)
{
Response.Redirect("page2.aspx");
}
else
{
Label1.Visible = true;
}
}
The problem you are experiencing is because the followinq query Always returns one row even if there isn't a match in the database:
SELECT COUNT(*) FROM Account WHERE acc_username=....
If there is no match, you get a row with one column, value 0.
You are checking the number of rows returned when you should just be checking the return value.
Use this instead
int count = Convert.ToInt32(cmd.ExecuteScalar());
if(count==1)
{
Response.Redirect("page2.aspx");
}
else
{
Label1.Visible = true;
}
I know you said you don't want advice on security but just to be sure:
Don't store passwords plain text in a database. Always hash them using a salt.
Don't use string concatenation when building sql. Use parameters.
don't use ExecuteReader when you want to return a single value, use ExecuteScalar:
int count = int.Pares(cmd.ExecuteScalar().toString());
if(count >= 1)
{
Response.Redirect("page2.aspx");
}
else
{
Label1.Visible = true;
}
You should always use Paremeterized queries Using parameters in SQL statements
string username=TextBox1.Text;
string password=TextBox2.Text;
SqlConnection conn = new SqlConnection();
conn.ConnectionString = "Data Source=TEST-PC\\SQLSERVER2012;Initial Catalog=oncf;Integrated Security=True";
conn.Open();
SqlCommand cmd = new SqlCommand("SELECT * FROM Account WHERE acc_username=#username and
AND acc_password=#password", conn);
cmd.Parameters.AddWithValue("#username",username);
cmd.Parameters.AddWithValue("#password",password);
SqlDataAdapter da = new SqlDataAdapter(cmd);
DataTable dt = new DataTable();
da.Fill(dt);
if (dt.Rows.Count > 0)
{
Response.Redirect("page2.aspx");
}
else
{
Label1.Visible = true;
}
Try adding if (myreader.HasRows) before while(myreader.Read())
I am receiving the error, Must declare the scalar variable "#ID". Pointing at ExecuteScalar line. I looked on goggle and I think it has something to do with insert parameters for ID. Then again I read there could be a typo error. In my db I have declare column name as ID and Data Type as int, setting 'Is Identity' as Yes. As I am not going to insert ID column manually I think this is why I am having problem(s) and I don't know how to solve this problem.
What I am trying to do is insert username, login date and time. Update on the same column (same id column) when user logs out. Create a new column when user log in again and So on. I am using the similar code that I asked here and here when D Stanley helped me.
Thanks in advance if anyone can help me.
private int ID // forgot to add this.
{ get; set; }
private void btnLogin_Click(object sender, RoutedEventArgs e)
{
string value = cbRoles.Text;
switch (value)
{
case "Manager":
myCon.connectionString();
string dString = string.Empty;
SqlConnection thisConnection = myCon.dbCon;
SqlCommand nonqueryCommand = thisConnection.CreateCommand();
using (var command = myCon.dbCon.CreateCommand())
{
command.CommandText = "SELECT * FROM tblPrivileges";
command.Parameters.AddWithValue("UserName", (txtUserName.Text));
command.Parameters.AddWithValue("Password", (txtPassword.Text));
thisConnection.Open();
var reader = command.ExecuteReader(); //strcomp
{
if (reader.HasRows)
{
while (reader.Read())
{
txtUserName.Text = reader["UserName"].ToString();
txtPassword.Text = reader["Password"].ToString();
MainWindow gobackB = new MainWindow();
gobackB.Show();
LoginSample goback = new LoginSample();
goback.Hide();
}
}
else MessageBox.Show("You have entered incorrect credentials. Please try again", "error", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
myCon.dbCon.Close();
nonqueryCommand.CommandType = CommandType.Text;
nonqueryCommand.CommandText = "INSERT tblLoginLogTable (UserName, LoggedInDate, LoggedInTime) VALUES (#UserName, #LoggedInDate, #LoggedInTime)";
//nonqueryCommand.Parameters.AddWithValue("#ID", SqlDbType.Int); this did not work
//nonqueryCommand.Parameters["#ID"].Value = this.ID; this did not work
nonqueryCommand.Parameters.AddWithValue("#UserName", txtUserName.Text);
nonqueryCommand.Parameters.AddWithValue("#LoggedInDate", DateTime.Now);
nonqueryCommand.Parameters.AddWithValue("#LoggedInTime", DateTime.Now.ToString("HH:mm"));
thisConnection.Open();
nonqueryCommand.ExecuteNonQuery(); // error pointing here
nonqueryCommand.CommandText = "SELECT #ID = SCOPE_IDENTITY()";
int id = (int)nonqueryCommand.ExecuteScalar();
// int id = Convert.ToInt32(nonqueryCommand.ExecuteScalar()); this line did not work
this.ID = id;
myCon.dbCon.Close();
break;
The problem is still that you're trying to use the same "scope" with two different SQL commands. Even thought they are the same "variable" in C# in SQL they have different scope.
You'll need to execute both statements in one command and add the #ID parameter as an Output parameter in order to insert and get the identity out:
nonqueryCommand.CommandType = CommandType.Text;
nonqueryCommand.CommandText = "INSERT tblLoginLogTable (UserName, LoggedInDate, LoggedInTime) VALUES (#UserName, #LoggedInDate, #LoggedInTime); " +
"SELECT #ID = SCOPE_IDENTITY()";
nonqueryCommand.Parameters.AddWithValue("#UserName", txtUserName.Text);
nonqueryCommand.Parameters.AddWithValue("#LoggedInDate", DateTime.Now);
nonqueryCommand.Parameters.AddWithValue("#LoggedInTime", DateTime.Now);
nonqueryCommand.Parameters.Add("#ID",SqlDbType.Int).Direction = ParameterDirection.Output;
thisConnection.Open();
nonqueryCommand.ExecuteNonQuery();
int id = (int)nonqueryCommand.Parameters["#ID"];
Here:
nonqueryCommand.CommandText = "SELECT #ID = SCOPE_IDENTITY()";
your SQL assigns a value to a variable that is not declared. Since you are using ExecuteScalar, you probably just mean:
nonqueryCommand.CommandText = "SELECT SCOPE_IDENTITY()";
Note that you might need to cast it - it may come back as decimal.
How do I get the last id created in the policy table and store it into a variable so that I can use it for another table called backupspec table.
System.Data.SqlClient.SqlConnection dataConnection = new SqlConnection();
dataConnection.ConnectionString =
#"Data Source=JAGMIT-PC\SQLEXPRESS;Initial Catalog=SumooHAgentDB;Integrated Security=True";
System.Data.SqlClient.SqlCommand dataCommand = new SqlCommand();
dataCommand.Connection = dataConnection;
//tell the compiler and database that we're using parameters (thus the #first, #last, #nick)
dataCommand.CommandText = ("Insert Policies ( PolicyName, PolicyDesc, TimeAdded,OSFlag, CreateVSSSnapshot, CreateAuditLogForRecoveries, AllowUsersToOverwriteFiles, AutoHandleEnvErrors, NotifyOnEnvErrorCount, NotifyOnFileFailure, NotifyOnFileFailureCount, NotifyOnLackOfPCContact, NotifyOnLackOfPCContactDays, NotifyOnRecoveryFailures, NotifyOnRecoveryFailureReason) values (#pn,#pd,#TimeAdded,#os,#vss,#al,#uow,#hee,#oeec,#off,#offc,#oloc,#olocd,#orf,#orfr)");
dataCommand.Parameters.AddWithValue("#pn",pn);
dataCommand.Parameters.AddWithValue("#pd",pd);
dataCommand.Parameters.AddWithValue("#TimeAdded",TimeAdded);
dataCommand.Parameters.AddWithValue("#os",os);
dataCommand.Parameters.AddWithValue("#vss",vss);
dataCommand.Parameters.AddWithValue("#al",al);
dataCommand.Parameters.AddWithValue("#uow",uow);
dataCommand.Parameters.AddWithValue("#hee",hee);
dataCommand.Parameters.AddWithValue("#oeec",oeec);
dataCommand.Parameters.AddWithValue("#off",off);
dataCommand.Parameters.AddWithValue("#offc",offc);
dataCommand.Parameters.AddWithValue("#oloc",oloc);
dataCommand.Parameters.AddWithValue("#olocd",olocd);
dataCommand.Parameters.AddWithValue("#orf",orf);
dataCommand.Parameters.AddWithValue("#orfr",orfr);
dataConnection.Open();
dataCommand.ExecuteNonquery();
dataConnection.Close();
ArrayList jaja = (ArrayList)Session["BackupSpecList"];
for (int i = 0; i < jaja.Count; i++)
{
BackupSpecEntry bsp = (BackupSpecEntry)jaja[i];
string path = bsp.path;
string inclExcl = bsp.inclExcl;
byte inclExclFlags = bsp.inclExclFlags;
bool indexContents = bsp.indexContents;
int serverBackupSpecId = bsp.serverBackupSpecId;
int freq = bsp.freq;
int retention = bsp.retention;
int policyID =DONT KNOW HOW TO GET THIS VALUE;
long specChangeTime = 0;
long backupTime = 0;
dataCommand.CommandText = ("Insert BackupSpec (PolicyID, Path, ServerBackupSpecID, Freq, Retention, InclExclFlags, InclExcl, IndexContents, SpecChangeTime, BackupTime) values (#policyID,#path,#serverBackupSpecId,#freq,#retention,#inclExclFlags,#inclExcl,#indexContents,#specChangeTime,#backupTime)");
dataCommand.Parameters.AddWithValue("#policyID", policyID);
dataCommand.Parameters.AddWithValue("#path", path);
dataCommand.Parameters.AddWithValue("#serverBackupSpecId", serverBackupSpecId);
dataCommand.Parameters.AddWithValue("#freq", freq);
dataCommand.Parameters.AddWithValue("#retention", retention);
dataCommand.Parameters.AddWithValue("#inclExclFlags", inclExclFlags);
dataCommand.Parameters.AddWithValue("#inclExcl", inclExcl);
dataCommand.Parameters.AddWithValue("#indexContents", indexContents);
dataCommand.Parameters.AddWithValue("#specChangeTime", specChangeTime);
dataCommand.Parameters.AddWithValue("#backupTime", backupTime);
dataConnection.Open();
dataCommand.ExecuteNonQuery();
dataConnection.Close();
}
I am getting error with the label id...
can some 1 help me with this..??
I am not getting the last policyID created after inserting please help...
Please help
Use scope_identity:
strSQL = "INSERT INTO Policies (...) VALUES (#vals....);SELECT #result = scope_identity()"
SQLCommand.CommandText = strSQL;
SQLCommand.Parameters.Add("#result", SqlDbType.Int);
SQLCommand.ExecuteScalar();
int id = SQLCommand.Parameters["#result"].Value;
You can use either SCOPE_IDENTITY or ##IDENTITY
SCOPE_IDENTITY:
strSQL = "INSERT INTO Policies (...) VALUES (#vals....);SELECT SCOPE_IDENTITY()";
SQLCommand.CommandText = strSQL;
IdReturned = SQLCommand.ExecuteScalar();
##IDENTITY:
strSQL = "INSERT INTO Policies (...) VALUES (#vals....);SELECT ##Identity";
SQLCommand.CommandText = strSQL;
IdReturned = SQLCommand.ExecuteScalar();
For the differences between the two i recommend reading this article
If you do a INSERT INTO Policies() call first, in order to get the lastid, you could do something like this:
int lastId = 0;
using(SqlConnection Connection = new SqlConnection("(connection string)"))
{
string queryStatement =
"INSERT INTO dbo.Policies(fields) OUTPUT Inserted.LastID VALUES(....)";
using(SqlCommand Command = new SqlCommand(queryStatement, Connection))
{
Connection.Open();
lastId = Command.ExecuteScalar();
Connection.Close();
}
}
Use the OUTPUT ....... clause to return the newly inserted lastId.
Then go on and use that value in your main query.
Marc