I'm working on a project where I wish to be able to send commands to SQL Server and also run Queries. I've succeeded in returning a query to a listbox using Joel's very good tutorial here:
creating a database query METHOD
I am now trying to adapt this to execute some commands and then run a query to check the commands worked. My query is failing because I think the commands did not work.
Currently I am sending this:
MySqlCommand("CREATE TABLE #CSVTest_Data" +
"(FirstTimeTaken DATETIME," +
"LatestTimeTaken DATETIME," +
"Market VARCHAR(50)," +
"Outcome VARCHAR(50),"+
"Odds DECIMAL(18,2)," +
"NumberOfBets INT," +
"VolumeMatched DECIMAL(18,2),"+
"InPlay TINYINT)");
Into this:
private void MySqlCommand(string sql)
{
int numberOfRecords;
//string result;
using (var connection = GetConnection())
using (var command = new SqlCommand(sql, connection))
{
connection.Open();
numberOfRecords = command.ExecuteNonQuery();
}
MessageBox.Show(numberOfRecords.ToString());
}
My understand is that ExecuteNonQuery returns an integer of the number of rows effected. My message box shows a value of -1. Running the same command in SQL Server returns 'Command(s) completed successfully.' I would appreciate if somebody could tell me whether my MySqlCommand method looks OK and how I might return the SQL Server message that is output by running the function.
In order to obtain messages that are output to the Messages tab in SQL Server Management Studio, "the console" when executing SQL statements on SQL Server, it is necessary to hook into the InfoMessage event on the SqlConnection class:
using (var connection = GetConnection())
using (var command = new SqlCommand(sql, connection))
{
connection.InfoMessage += (s, e) =>
{
Debug.WriteLine(e.Message);
};
connection.Open();
numberOfRecords = command.ExecuteNonQuery();
}
Obviously you will need to handle the event differently from what I showed above, and there are other properties on the e parameter here as well, see SqlInfoMessageEventArgs for details.
NOW having said that, bear in mind that some of the messages output to the message tab in SQL Server Management Studio is generated by that program, and does not originate from the server itself, so whether that particular message you're asking about would show up through that event I cannot say for sure.
Additionally, in this particular type of SQL Statement, the correct return value from ExecuteNonQuery is in fact -1, as is documented:
For UPDATE, INSERT, and DELETE statements, the return value is the number of rows affected by the command. When a trigger exists on a table being inserted or updated, the return value includes the number of rows affected by both the insert or update operation and the number of rows affected by the trigger or triggers. For all other types of statements, the return value is -1. If a rollback occurs, the return value is also -1.
(my emphasis)
Change
var numberOfRecords = command.ExecuteNonQuery();
to
var numberOfRecords = command.ExecuteScalar();
Also, please have a look at SqlCommand Methods
You Should use ExecuteScalar.
ExecuteScalar is typically used when your query returns one value.
ExecuteNonQuery is used for SQL statements like update,insert,create etc.
So change it to
numberOfRecords = (int)command.ExecuteNonQuery();
Here is a comment from MSDN on ExecuteNonQuery:
For UPDATE, INSERT, and DELETE statements, the return value is the
number of rows affected by the command. ... For all other types of
statements, the return value is -1.
Since you are executing neither UPDATE nor INSERT nor DELETE - you are receiving -1 even though operation is successful. Basically you can assume that if no SqlException was thrown - your CREATE statement worked.
numberOfRecords=(int) command.ExecuteScalar();
Related
Im trying to re-work my SQL queries into safe alternatives to prevent SQL injection attacks but having difficulty pulling from the database using these parameters.
This is C#, using an MS Access database and OleDB connections.
I have the following query:
OleDbCommand cmd = new OleDbCommand("Select ?, [ID] FROM Agent_Skills", conn);
cmd.Parameters.Add(new OleDbParameter("?", OleDbType.VarChar, 100) {
Value = ScriptName
});
reader = cmd.ExecuteReader();
while (reader.Read())
{
if (reader[ScriptName].ToString() != "0")
{
selectedSkillIDs.Add(reader["ID"].ToString());
selectedPriority.Add(reader[i].ToString());
}
}
When I run this query I get an IndexOutOfRange exception from
reader[ScriptName].ToString()
The ScriptName variable is required as this is going to change multiple times through the scripts daily cycle so cannot be static.
If I swap out ScriptName to [1] I'll get each row as the loop iterates through my results, but i need these to correlate to each other as I need specific pairs of data.
I'm at a loss as to how to use parameters in a query like this to actually get the results, Google brings up plenty of results for UPDATE, DELETE and other input queries.
Do I need to use parameters like this for a simple data return query? If so how do I go about this?
First, just to be clear, I recognize that ExecuteNonQuery should only be used for UPDATE, INSERT, or DELETE statements, and that for all other types of statements, such as SELECT, the return value is -1.
My question is, why does the following stored procedure:
CREATE PROCEDURE `ExampleProc`(IN Name varchar(60), OUT ID bigint(20), OUT SP varchar(255))
BEGIN
SELECT id, sp INTO ID, SP FROM users WHERE username = Name;
END
When executed using ExecuteNonQuery:
using (var conn = new MySqlConnection("Secret"))
{
conn.Open();
using (var cmd = new MySqlCommand("ExampleProc", conn) { CommandType = CommandType.StoredProcedure })
{
cmd.Parameters.AddWithValue("Name", request.Name).MySqlDbType = MySqlDbType.VarChar;
cmd.Parameters.Add("ID", MySqlDbType.Int64).Direction = ParameterDirection.Output;
cmd.Parameters.Add("SP", MySqlDbType.VarChar).Direction = ParameterDirection.Output;
var returnVal = cmd.ExecuteNonQuery();
}
}
Yield a 0 in returnVal when a row with Name is not found, and a 1 if it is found? Based on all the documentation I have read, since the stored procedure contains a single SELECT statement, I should be seeing -1 being returned no matter what. Instead, it's returning the number of rows affected/found, which doesn't make sense according to the documentation.
Lastly, I've also tried using just "SELECT *" instead of "SELECT id, sp INTO ID, SP". That seems to always return 0. Still not the -1 that I am expecting.
Let's understand the working of the ExecuteNonQuery API in the ADO.Net, as you have understood from the documentation that it shall provide the number of Rows impacted as part of DML query and -1 for the Select query.
However the internal working is such that in case of Sql Server, default setting is Set NoCount Off helps in writing to the TDS protocol, the number of modified rows and same is read by the Ado.Net APIs, thus the correct result, this is the result of native binary integration of ado.net with Sql Server. more details at the following links:
Ado.Net TDS Protocol
SET NOCOUNT ON usage
When it comes to other database, like MySql, Oracle, there's no guarantee of the same behavior, as their integration is not at same level as Sql Server. This is the main reason for discrepancy, posted in the question, to make it consistent across the database, you may want to rely of a Output Parameter to automatically fill the value internally in the procedure, since every database has mechanism to figure out the number of rows updated
This is bugging me. Something so simple and it does not work. What could I be doing wrong?
Here is the code I am using ...
string strSQL = "SELECT ac.ContactEmail AS fldEmail FROM vwApprenticeshipContactDetails ac WHERE (ac.ApprenticeshipID = #ApprenticeshipID)";
SqlCommand cmd2 = new SqlCommand(strSQL, cnn);
cmd2.Parameters.Add("#ApprenticeshipID", SqlDbType.Int).Value = wsAppID;
Object fldEmail = cmd2.ExecuteScalar();
Believe it or not, the above returns what looks like an array when I debug and look at the value of 'fldEmail'. In otherwords it's empty, nothing or null (cannot tell as the output window shows an array). I have run this directly in SQL server and I get the email showing fine. Could the '#' character inside the database field be causing something here?
The email I expected back is 'g.somebody#domain.com.au', which to me looks ok.
UPDATE
I replaced the ContactEmail field above, with another column name from the database and it comes back fine. Not sure why emails are an issue. The datatype by the way here is VARCHAR(50).
UPDATE
In debug mode I noticed it returns system.DBNull data type (and digging deeper it returns empty), but why? I ran the exact same query with the correct parameter in SQL server and I get a email showing. Strange
Thanks
Solved it!
For some reason (and I am a noob when it comes to email objects in C#), but an ExecuteScalar is not properly populating as it was not "read" first. What I mean by this is that when I changed ExecuteScalar to an SqlReader, it worked because I am "forcing" the read of the SQL before testing it.
So my code looks now like this and it returns an email!
strSQL = #"SELECT TOP 1 ContactEmail FROM vwApprenticeshipContactDetails WHERE ApprenticeshipID = #ApprenticeshipID";
SqlCommand cmd2 = new SqlCommand(strSQL, cnn);
cmd2.Parameters.Add("#ApprenticeshipID", SqlDbType.Int).Value = wsAppID;
using (SqlDataReader rdr = cmd2.ExecuteReader())
{
string fldEmail = "support#domain.com.au"; //this is as a default in case the sql above does not return a value
while (rdr.Read())
{
fldEmail = rdr.GetSqlValue(0).ToString();
}
}
I was avoiding SqlReader initially because I thought they were specifically designed for when reading in more than one row of data. In my case I know the parameter given only returns one row or nothing at all.
Thanks
At the first change select to top 1 for performance reason.
string strSQL = "SELECT Top 1 ac.ContactEmail AS fldEmail FROM ...
If you dont have any result in query, you will get null value as result.
You can check how many records returned by query in sql server profiler.
Also you can check your connection string to make sure are you connected to correct database.
I have a stored procedure in a SQL Server 2005 database that's accessed by ASP.NET / C# code.
The parameters for the stored procedure are defined in the typical manner:
Try
{
SqlCommand cmd = new SqlCommand("mystoredprocedure",myConnection)
cmd.Parameters.Add(new SqlParameter("#p1"), SqlDbType.Int))
cmd.Parameters["#p1"].Value = myvalue (usually form inputs)
.
.
.
myConnection.Open()
cmd.ExecuteNonQuery()
}
catch (Exception xx)
{
lblError.Text = xx.Message;
}
finally
{
myConnection.Close();
}
The problem is my data never updates, though the stored procedure doesn't throw any errors. I've checked the procedure in SQL Server, and when I call it directly through SQL Server the proc makes my updates.
I'm tearing my hair out on this one since it worked fine for a long time, then stopped working. I've checked the usual culprits (db connection is pointing to the right database, the procedure seems to work fine in SQL Server, and I've commented out the few new parameters I've created)
Any thoughts on what could be causing this? It's strange that the same procedure works in SQL Server but not through my code without throwing any errors. (To be sure, I put a return value in the stored procedure, and the return value indicates I'm not rolling back)
EDIT
Code for the stored procedure:
BEGIN TRANSACTION
--get count
update dbo.myTable set
val1=#val1,
val2=#val2,
.
.
.
WHERE ID=#MyID
SET #Err = ##ERROR
--if error rollback transaction
IF #Err <> 0
BEGIN
ROLLBACK TRANSACTION
GOTO ErrorHandler
END
Select #ReturnCode = 1
COMMIT TRANSACTION
RETURN
ErrorHandler:
--unknown error
Select #ReturnCode = 0
RAISERROR (#Err, 16, 1 ) WITH LOG
RETURN -100
EDIT
When I parse cmd.ExecuteNonQuery, I get -1 as a result. Still trying to figure out why the C# code is doing this but not throwing any errors. Shouldn't ExecuteNonQuery return the number of rows affected by the stored procedure?
EDIT
Using TFS I've stepped back in my history a couple of revs - it seems like there's an additional field I added recently that's breaking the query. When I comment out that field and call my sproc, it works fine.
I don't get why, though - it's just a varchar(255) field. I've got plenty of other fields in this database that are set up in similar ways. Any thoughts as to why this would be a problem?
EDIT
Using SQL Profiler, I can see the statement execute AND commit...but still, no data updates. I'm wondering whether I need to trash the sproc and start over again?
Run the stored procedure without the .Net code (i.e directly in SQL Server Management Studio) and see whether the changes are updated or not. Probably you are missing an explicit commit statement.
May sound stupid, but if you are out of disk space I've seen SQL server go through the motions, no errors but your data does not get saved. Easy to check too!
Try to set CommandType like that :
SqlCommand cmd = new SqlCommand("mystoredprocedure",myConnection)
cmd.CommandType = CommandType.StoredProcedure
Try to use:
SqlCommand cmd;
cmd.CommandType = System.Data.CommandType.StoredProcedure;
or you maybe use:
CALL mystoredprocedure(#ParameterName)
as SQL-Text
try this !
SqlCommand cmd = new SqlCommand(string.Format("mystoredprocedure('{0}')",inputValue),myConnection)
Try to run sp directly from SQL SERVER and remove error handling from stored procedure to get the exact error in .Net.
Can you try one simple thing for me?
Change your stored procedure to accept no parameter. Change logic of stored proc to just insert one row in one table and then change your code to call that procedure and find out whether call is successful or not.
I think that you use a wrong way to call a stored procedure. The value -1 returned by ExecuteNonQuery is because of the statement: if INSERT/UPDATE/DELETE the value is the number of rows affected, -1 if there's a rollback. In all the other cases the return value is -1 (your case, I think).
The SqlCommand constructor when used with the two parameters SqlCommand(String, SqlConnection) needs that the first one is a generic query so you can run something like "exec myProcedure #par1" and the parameters can be set e.g.:
Dim par1 As New SqlParameter("#par1", SqlDbType.VarChar, 50)
par1.Value = "DummyValue"
cmd.Parameters.Add(par1)
But in the ideal situation, when you've to call a stored than you've to set the CommandType to "StoredProcedure", like then MSDN suggests in this link http://msdn.microsoft.com/it-it/library/yy6y35y8.aspx.
(I will show you VB code... sorry.. it's easily converted to C# with tools like this.)
If you want to read a value from the stored procedure that you run, you can populate a datased (for example, "ds") using a SqlAdapter -this is only an example-:
Dim da As New SqlDataAdapter(cmd)
da.Fill(ds, "result")
If ds.Tables.Item("result").Rows.Count > 0 Then
Return True
Else
Return False
End If
If you don't need to read anything you can simply use:
cmd.ExecuteNonQuery()
Surely you've to tell that you want to launch a stored procedure:
cmd.CommandType = CommandType.StoredProcedure
Hope to be useful to you...
Bye, Massimo.
I think you are missing Command Type while creating Command object.. Set command type of command object as StoreProcegure and then check again.. Since if u dont set command type then ur query execute but u wont get any error and required result also.
From your code I can see that the parameter name you are passing is not the same that you are using in you procedure.
In C# ADO.NET, how can I tell if a sproc returned 0 rows versus the sproc had no commands to run on TSQL?
Let me lay it out in full. In TSQL I have (when I open this in SSMS)
ALTER PROC mySproc
#myvar VARCHAR(10)
AS
/* commented out on dev system - uncomment on production */
/* guess what didn't happen */
/* careful readers will note that we _normally_
throw an exception when we do this. this sproc was missing said exception. */
When run in SSMS mySproc 'value' this says "Command(s) completed successfully." Under better circumstances (aka just no data returned but the body of the sproc is actually running), it would return what looked like a table but with no rows.
and in C#:
using( SqlDataReader reader = cmd.ExecuteReader() ){
//reader.HasRows == false
}
So, that's what I ran into, and without using "sql reflection" (ergo reading the actual script in the database) and without using an ORM (because then I would know that I was sending the right commands, but we have our reasons for using sprocs, of course) ...
How can I tell that I got a result of "Command(s) completed successfully." instead of a potential table with just no rows?
I originally put this in a comment but it is more appropriate as an answer
You can use the FieldCount property on the reader to determine the number of columns. For example:
using( SqlDataReader reader = cmd.ExecuteReader() )
{
if (reader.FieldCount > 0)
{
// there are columns
}
else
{
// there are no columns, so stored proc returning no results set
}
}
From the MSDN page on FieldCount
Executing a query that, by its nature, does not return rows (such as a DELETE query), sets FieldCount to 0. However. this should not be confused with a query that returns 0 rows (such as SELECT * FROM table WHERE 1 = 2) in which case FieldCount returns the number of columns in the table, including hidden fields. Use VisibleFieldCount to exclude hidden fields.
I would suggest auditing SQL Server and beating the crap out of developers that actually create SPROCs without bodies. I know of no way to determine stupidity in SQL Server from a reader other than actually issuing an audit when there are no rows (running something like sp_helptext and determine if there is a body?). It is hard to protect from sloppiness. Sorry!