SqlCommand Parameter not working - c#

When I execute the following command in SSMS I get the results I expect -
SELECT *
FROM [Event]
WHERE Active = 'True'
AND [Name] LIKE '%P%'
i.e. All the Events whose Name contains a P are displayed
However, when I build this command in my WinForms app written in C# I always get an empty datatable.
I am using the following code -
string sqlText = "SELECT * " +
"FROM Event " +
"WHERE Active = #Active";
SqlCommand sqlCom = new SqlCommand();
sqlCom.Parameters.Add("#Active", SqlDbType.Bit).Value = active;
if (txtSearchName.Text != null && txtSearchName.Text.Length > 0)
{
sqlText += " AND [Name] LIKE '#Name'";
string value = "%" + txtSearchName.Text + "%";
sqlCom.Parameters.Add("#Name", SqlDbType.VarChar).Value = value;
}
sqlText += " ORDER BY [Date] DESC;";
sqlCom.CommandText = sqlText;
DataTable table = Express.GetTable(sqlCom);
DataView view = new DataView(table);
DataTable show = view.ToTable(true, "ID", "Date", "Name");
dataEventsFound.DataSource = show;
Please note Express.GetTable is a method that simply adds a SQLConnection and uses a DataReader to fill and return a DataTable. I do not believe the fault lies in that Method as it is used hundreds of times throughout this and other applications I have written.
I think the error is something to do with the Command Text and the #Name Parameter, but I can't determine exactly what the problem is, and why my DataTable is always empty.

Remove single quote from between #Name parameter.
sqlText += " AND [Name] LIKE #Name";

After this line : sqlCom.CommandText = sqlText;
You should add this line: sqlCom.ExecuteNonQuery();

Related

Is there a function in SQL Server to convert to a decimal number?

I have a cell value in a column that is "12,000". And I want to change to "11,000" and display it... but it only displays a blank space. And in the database the value appears NULL.
In the database the type is Decimal(18,3).
My code in C# is like this:
decimal dec = Convert.ToDecimal(dgvRow.Cells[16].Value.ToString());
string query = "UPDATE cabecdoc SET CDU_Peso = TRY_CONVERT(DECIMAL(18,3),'" + dec + "' ) WHERE Id = '" + idDoc + "'";
If I do the query:
UPDATE CabecDoc
SET CDU_Peso = TRY_CONVERT(DECIMAL(18,3), '11.000')
WHERE Id = 'fb9668a9-46fa-11ec-9494-00155d01b010'
in Microsoft SQL Server - it works... but in my program in C# it displays a blank space value.
Ok, assuming the text value will NOT have the $ (or currency character), then this will work:
string strSQL = "UPDATE cabecdoc " +
"SET CDU_Peso = #Peso " +
"WHERE Id = #ID";
using (SqlCommand cmdSQL = new SqlCommand(strSQL, conn))
{
conn.Open();
cmdSQL.Parameters.Add("#Peso", SqlDbType.Decimal).Value = dgvRow.Cells[16].Text;
cmdSQL.Parameters.Add("#ID", SqlDbType.NVarChar).Value = idDoc;
cmdSQL.ExecuteNonQuery();
}
So if the cell has:
12000
12,000
12,000.00
Then the above will work fine.
However, if the cell is to have:
$12,000.00
Then you need to use the globalization converters for this.
Say like this:
// -- using System.Globalization;
Decimal TestNum = 0;
decimal.TryParse(TextBox1.Text,
NumberStyles.Currency,
CultureInfo.CurrentCulture.NumberFormat, out TestNum);
Now, if the converter fails, then the TestNum will not be changed (the "out" return value in above), and then we now have this:
string strSQL = "UPDATE cabecdoc " +
"SET CDU_Peso = #Peso " +
"WHERE Id = #ID";
using (SqlCommand cmdSQL = new SqlCommand(strSQL, conn))
{
conn.Open();
cmdSQL.Parameters.Add("#Peso", SqlDbType.Decimal).Value = TestNum;
cmdSQL.Parameters.Add("#ID", SqlDbType.NVarChar).Value = idDoc;
cmdSQL.ExecuteNonQuery();
}
And while the first will work as long as no currency character such as "$"?
Well, it will work, but first example does not handle "" (empty string).
so, you could say use this:
string MyPeso = dgvRow.Cell[16].Text;
if (MyPeso == "")
MyPeso = "0";
....
cmdSQL.Parameters.Add("#Peso", SqlDbType.Decimal).Value = MyPeso;
Also, as noted, not only is using paramters a lot easier - you don't even have to know (or think) if you need to surround the values - so the sql is much easier to read, and as noted, it is sql injection safe.
eg this:

Get the identity of the last updated row in SQL via C#

I am trying to first create a new row in my SQL Compact Edition database via C# and then I want to update the same row with information in my radiobuttons. I have an "ID" column in the database which is auto incremental.
So I tried to assign its value to a variable using ##Identity and call it in the update query but it doesn't work. I've tried MAX to find the max value in ID column which will be the latest row but it still didn't work. Here's my code.
con.Open();
string sqlAdd = "Insert into MembersTable ([First Name],Surname,[Middle Name])
Values('"+txtFirstName.Text+"','"+txtSurname.Text+"','"+ txtMiddleName.Text+"')";
string IDIdentifier = "Select ##Identity AS TempID";
string sqlgenderM = "Update MembersTable set Gender='M' where ID='" + DC.ID + "'";
string sqlgenderF = "Update MembersTable set Gender='F' where ID='" + DC.ID + "'";
com = new SqlCeCommand(sqlAdd, con);
com.ExecuteNonQuery();
SqlCeCommand com1 = new SqlCeCommand(IDIdentifier, con);
SqlCeDataReader dr1 = com1.ExecuteReader();
if (dr1.Read())
{
DC.ID = dr1["TempID"].ToString();
}
{
if (rbGenderMale.Checked == true)
{
SqlCeCommand gendercom = new SqlCeCommand(sqlgenderM, con);
gendercom.ExecuteNonQuery();
}
else if (rbGenderFemale.Checked == true)
{
SqlCeCommand gendercom = new SqlCeCommand(sqlgenderF, con);
gendercom.ExecuteNonQuery();
}
}
The fields (First Name, Middle Name, Surname) get updated but the Gender columns don't. What am I doing wrong?
Thanks to #Soner I used:
int.TryParse(dr1["TempID"].ToString(), out Identity);
string IdentityS = Identity.ToString();
and replaced DC.ID with IdentityS
Now it works perfectly.

Getting Result From Select Command SQL Server

I am trying to get the result from a select command:
string strName = dtTable.Rows[i][myName].ToString();
string selectBrand = "SELECT [brand] FROM [myTable] WHERE [myName] = '" + strName + "'";
SqlCommand sqlCmdSelectBrand = new SqlCommand(selectBrand , sqlConn);
sqlCmdSelectBrand .Connection.Open();
sqlCmdSelectBrand .ExecuteNonQuery();
string newBrand = Convert.ToString(sqlCmdSelectBrand .ExecuteScalar());
sqlCmdSelectBrand .Connection.Close();
The select works, I have executed it in SQL Studio, but it does not assign to my variable on the second to last line. Nothing gets assigned to that variable when I debug it...
Any advice?
Your approach to read data returned from a SELECT query is (in this particular context) a bit wrong. Usually you call ExecuteReader of the SqlCommand instance to get back your data.
string strName = dtTable.Rows[i][myName].ToString();
string selectBrand = "SELECT [brand] FROM [myTable] WHERE [myName] = #name";
using(SqlCommand sqlCmdSelectBrand = new SqlCommand(selectBrand , sqlConn))
{
sqlCmdSelectBrand.Parameters.Add(
new SqlParameter("#name", SqlDbType.NVarChar)).Value = strName;
sqlCmdSelectBrand .Connection.Open();
using(SqlDataReader reader = sqlCmdSelectBrand.ExecuteReader())
{
if(reader.HasRows)
{
reader.Read();
string newBrand = reader.GetString(reader.GetOrdinal("Brand"));
..... work with the string newBrand....
}
else
// Message for data not found...
sqlCmdSelectBrand .Connection.Close();
}
}
In your context, the call to ExecuteNonQuery is not required because it doesn't return anything from a SELECT query. The call to ExecuteScalar should work if you have at least one record that match to the WHERE condition
Notice also that you should always use a parameterized query when building an sql command text. Also if you think to have full control of the inputs, concatenating string is the open door to Sql Injection

More efficient way of running multiple update queries on an Access database?

I have multiple queries like this right now which involve updating different fields of the same row in an Access database:
//Update database
string updatequery = "UPDATE [table] SET [Last10Attempts] = ? WHERE id = ?";
OleDbConnection con = new OleDbConnection(#"Provider=Microsoft.ACE.OLEDB.12.0;" + #"Data Source=" + "database.accdb");
con.Open();
OleDbDataAdapter da = new OleDbDataAdapter(updatequery, con);
var accessUpdateCommand = new OleDbCommand(updatequery, con);
accessUpdateCommand.Parameters.AddWithValue("Last10Attempts", last10attempts);
accessUpdateCommand.Parameters.AddWithValue("ID", currentid + 1);
da.UpdateCommand = accessUpdateCommand;
da.UpdateCommand.ExecuteNonQuery();
//update last10attemptssum
updatequery = "UPDATE [table] SET [Last10AttemptsSum] = ? WHERE id = ?";
accessUpdateCommand = new OleDbCommand(updatequery, con);
accessUpdateCommand.Parameters.AddWithValue("Last10AttemptsSum", counter);
accessUpdateCommand.Parameters.AddWithValue("ID", currentid + 1);
da.UpdateCommand = accessUpdateCommand;
da.UpdateCommand.ExecuteNonQuery();
//increment totalquestionattempt
updatequery = "UPDATE [table] SET [total-question-attempts] = ? WHERE id = ?";
accessUpdateCommand = new OleDbCommand(updatequery, con);
accessUpdateCommand.Parameters.AddWithValue("total-question-attempts", questionattempts + 1);
accessUpdateCommand.Parameters.AddWithValue("ID", currentid + 1);
da.UpdateCommand = accessUpdateCommand;
da.UpdateCommand.ExecuteNonQuery();
con.Close();
I was wondering if there is a more efficient way of running these update queries - ie. combining them into one query.
There is no need to use an OleDbDataAdapter in your context above. You could use a simple command and execute it
Said that, an Update sql statement can update more than one field. Just write
string updatequery = #"UPDATE [table] SET [Last10Attempts] = ?,
[Last10AttemptsSum] = ?,
[total-question-attempts] = ?
WHERE id = ?";
using(OleDbConnection con = new OleDbConnection(.........))
using(OleDbCommand cmd = new OleDbCommand(updatequery, con))
{
con.Open();
cmd.Parameters.AddWithValue("Last10Attempts", last10attempts);
cmd.Parameters.AddWithValue("Last10AttemptsSum", counter);
cmd.Parameters.AddWithValue("total-question-attempts", questionattempts + 1);
cmd.Parameters.AddWithValue("ID", currentid + 1);
cmd.ExecuteNonQuery();
}
The only thing to keep present when working with OleDb is the fact that the parameters are used in the exact order in which the parameter placeholder appears in the command text. So they should be added to the parameter collection in the order expected by the command text

How can I call a SQL function in C#?

I have created a function in SQL, now I need to use that function in my C# application.
I tried using something like this, but it seems I'm doing it wrong since I'm getting:
Must declare the scalar value '#2064734117'
...when I give 2064734117 as the first parameter and 1 as the second parameter. Here is the code I'm talking about:
SqlConnection con = new SqlConnection(clsDb.connectionString);
string query = string.Format("select Function1(#{0},#{1}) ",
int.Parse(e.CurrentRow.Cells["CodeMeli"].Value.ToString()),1);
con.Open();
SqlCommand cmd = new SqlCommand(query,con);
SqlDataAdapter READER = new SqlDataAdapter();
READER.SelectCommand = cmd;
DataTable table = new DataTable();
READER.Fill(table);
radGridView1.DataSource = table;
con.Close();
And my function takes two integer parameters and returns a table. I checked it in Visual Studio and it worked, but I couldn't get it to work in my application.
And this is my function declaration:
ALTER FUNCTION dbo.Function1
(
/*
#parameter1 int = 5,
#parameter2 datatype
*/
#ID int,
#clsTypeID int
)
RETURNS TABLE/* #table_variable TABLE (column1 datatype, column2 datatype) */
AS
/*BEGIN */
/* INSERT INTO #table_variable
SELECT ... FROM ... */
RETURN SELECT * FROM tblCLASS2
WHERE STNID = #ID AND CLASSTYPEID = #clsTypeID
/*END */
/*GO*/
Your SQL is a bit off, it should be:
string query = string.Format("select * from dbo.Function1({0},{1});", int.Parse(e.CurrentRow.Cells["CodeMeli"].Value.ToString()),1);
You might want to use SqlParameter-objects to prevent sql injections:
string query = "select * from dbo.Function1(#pa1,#par2);";
cmd.Parameters.Add("#par1", SqlDbType.Int).Value = int.Parse(e.CurrentRow.Cells["CodeMeli"].Value.ToString());
cmd.Parameters.Add("#par2", SqlDbType.Int).Value = 1;
At a glance, the first thing I can see is that you aren't specifying the object owner / schema; that is required for functions, so it should be select dbo.Function1(...
Second: look at what your call to string.Format generates; that is generating #1 and #n for n another integer, but that is not a valid parameter name. Which is handy, because
Third: you didn't add any parameters
Fourth: for a table UDF (rather than a scalar UDF), you must select * from dbo.Function1(..., not just select dbo.Function1(...
You can do something like this:
myConn.Open();
//generating the new command for our database
SqlCommand cmd = new SqlCommand();
cmd.CommandType = CommandType.Text;
cmd.CommandText = "SELECT OBJECTID_1, NDNT as theAddress, MIN(ABS(x - " + double.Parse(x.ToString()) + ") + ABS(y - " + double.Parse(y.ToString()) +")) from dbo.DWH_OUTPUT GROUP BY OBJECTID_1,NDNT HAVING (MIN(ABS(x - " + double.Parse(x.ToString()) + ") + ABS(y - " + double.Parse(y.ToString()) + ")) = (Select MIN(ABS(a.x - " + double.Parse(x.ToString()) + ") + ABS(a.y - " + double.Parse(y.ToString()) + ")) from dbo.DWH_OUTPUT a ) )";
cmd.Connection = myConn;
//getting some more ado.net objects
SqlDataAdapter da = new SqlDataAdapter();
DataSet ds = new DataSet();
da.SelectCommand = cmd;
da.Fill(ds, #"Addresses");
if (ds.Tables[0].Rows.Count > 0)
{
theAddress = ds.Tables[0].Rows[0][#"theAddress"] + #" (proximity address)";
}
myConn.Close();
Note how in this example, you set the SqlCommand's CommandType to CommandType.Text. Specify your command parameters (i.e. the select function in your code snippet), and then populate the dataset with the Fill method. Then you can pluck out the values from the rows as you ordinarily would with standard ado.net.
If you need to call a stored proc, have a look at this:
How do I call a TSQL function from ado.net
You need fully qualified name of function with owner/schema name
A working sample available at following link:

Categories