In my C# code, I'm trying to open a connection to a SQL Server database and get a count. I've copied the code (barring the query itself) from another application I wrote that works fine. I've run the query -- both the version I constructed, and the query the code constructs (I use variables) -- and they both work fine, returning an identical count. I'm just not getting anything back.
I've put in breakpoints and checked my connection and query, and it is what I expect. When I Googled this, I just got results for people having entirely different problems, so I'm stumped. I don't know why I'm not getting anything back. Where is the error?
SqlConnection RRconnection = new SqlConnection();
RRconnection.ConnectionString = "Data Source=;Initial Catalog=;User id=;Password=";
RRconnection.Open();
string ridQuery = "SELECT COUNT (t.RxTimeStamp) FROM...";
SqlCommand query = new SqlCommand(ridQuery, RRconnection);
SqlDataReader data = query.ExecuteReader();
if (data.Read())
transcount = Convert.ToInt32(data.GetValue(0));
In case the above paragraph wasn't clear, I'm expecting the query to return a count of 1592064 (for the specific instance I'm testing), and instead there's no data in data.
Probably not related, but my query includes three inner joins. It's the first time I've done that many, so maybe that could be the problem? Except if that was causing the problem, it wouldn't work when I run the query?
have you tried using SqlCommand.ExecuteScalar Method
SqlCommand query = new SqlCommand(ridQuery, RRconnection);
Int32 transcount = (Int32) query.ExecuteScalar();
Update
I am not sure why your query did not work, maybe try giving the count column an alias:
string ridQuery = "SELECT COUNT (t.RxTimeStamp) AS RxCount FROM ...";
It is best to use ExecuteScalar if the query returns a single value.
ExecuteScalar; return object value so must be Convert to the correct type
int totalCount = Convert.ToInt32(cmd.ExecuteScalar());
So, to the you question
It's a ExecuteReader don't use to get a Select Count result, but there's no problem this code. You can see the total count value for this code.
Note : The SqlDataReader Object is a stream-based and forward-only retrieval of query results from the Data Source.
The Read method processes only one row in memory then overwrites old record when itterate next row.
ExecureReader; In your case , reader first row and first column filled query result, other rows and column set to null.
if(reader.Read())
{
int totalRows = Convert.ToInt32(reader.GetValue(0)); //convert returned value
}
Note : use column name and use reader spesific type convert method is my best choice.
reader.GetInt32(reader.GetOrdinal("TotalCount"))
Advice,
Don't use ExecuteReader read for single value
Use ExecuteScalar then cast result to correct type
Use IDisposible objects (Connection , Command , Reader) inside to using code blog. using block close and dispose current object.
You should not read using code blog on column index value
Related
I've got an error when i'm using MySql Stored Procedure and
Data Access Layer (MVC)have a method used to read data from database using MySqlDataAdapter and MySqlParameter and DataTable to read data called storedatai was tested this method before in a login controller and it's work as well : the main error here when i need to store last accountId using MySql last_insert_id() function but it always returns null value i tried to Convert it to int then add 1 in every time form opening , but that is useless because it's returns an error
System.ArgumentException: 'Parameter 'accountId' not found in the collection.'
here is the method in Data Access Layer that i use it to store data from database
public DataTable storeData(string trChannel,MySqlParameter[] list_OF)
{
InitializeDb();// Database configration method
MySqlCommand Transmeter = new MySqlCommand
{
CommandType = CommandType.StoredProcedure,
CommandText = trChannel,
Connection = dbcon // dbcon its Connection string comming from DAL
};
if (list_OF != null)
{
Transmeter.Parameters.AddRange(list_OF);
}
MySqlDataAdapter massenger = new MySqlDataAdapter(Transmeter);
DataTable _mainContainer = new DataTable();
massenger.Fill(_mainContainer);
disConnect();
return _mainContainer;
}
The stored procedure that i used it like this after i tried to use last_insert_id() and i failed so i change the sp to this and its work and give me result when i call it inside MySQL Server
CREATE DEFINER=`root`#`localhost` PROCEDURE `getAccountId`(out accountId int(5))BEGIN select Max(acotId)from accounts;set #accountId=last_insert_id(acotId); END
In this case the server going throw an error
Unknown "acotId" in fields list
but i don't need to fix it because it's give me a result , so when i called this procedure inside application using a method that return a DataTable value give the first error that i wrote it before System.ArgumentException: 'Parameter 'accountId' not found in the collection.' for the record i got more than five methods contains the same error most of them with input or output parameter and most of them without ..
public DataTable getAcotId()
{
DataTable pdbContainer = new DataTable();
pdbContainer = _socket.storeData("getAccountId",null);//_socket it's a link to data access layer
return pdbContainer;
}
I tried also method like this with a parameter and it's also didn't works
public DataTable getCusttId()
{
MySqlParameter[] parCut = new MySqlParameter[1];
parCut[0] = new MySqlParameter("?custId", MySqlDbType.Int16, 5) ;
parCut[0].Direction = ParameterDirection.Output;
DataTable pdbContainer = new DataTable();
pdbContainer = _socket.storeData("getCustomerId", parCut);
return pdbContainer;
}
Finally i want to convert the result from this methods to int and i think about using this code
int customerFinalId = getCustId.Rows.Fields[i].Feilds<int>("custId")
That is all guys and i'm dire to need help immediately for more necessary
Thank you a lot for helping me ..
You got MySQL, that is a problem for this problem. It is very common that you need to figure out "the Primary Key of the thing you just inserted". Usually to update the (G)UI.
If this was SQL, the OUTPUT clause would be your friend. This thing alone is worth its memory footprint in gold. But MySQL does not have any equivalent Syntax. That means you have to do it the hard way.
When figuring this value out, it is very important to guard against race conditions. The reliable way is to replace the implicit Transactions and Table locks of the DML statement with a explicit one that covers both the DML statement and the DQL Satement (SELECT) that follows.
Unfortunatley I am not that solid in MySQL Syntax, so somebody else will have to give you exact code.
There are multiple issues with your code
You NEED to provide all the parameters your SP expects or else it will just error out. Since you do not have any try and catch in your SP your entire query will bail on first error.
#accountId is a sql variable and you need to declare that if you want to use it
last_insert_id takes no parameter unless you want to set it explicitly which destroys the whole purpose of it in your example.
The answer here is the second method of getCusttId replacing the ? by #, which works for me.
I have seen a few questions asked about this kind of thing already, but whenever I try anything it just doesn't work with this exact instance. I have a remote SQL SERVER database set up, and it has over 2000 rows.
This is my C#:
command.Connection = connectionString;
command.CommandType = CommandType.Text;
command.CommandText = "SELECT IconUrl FROM Items WHERE DefIndex IN " + iconSqlString;
connection.Open();
string iconString = command.ExecuteScalar().ToString();
connection.Close();
I do have the SqlConnection set up and all that, I just don't think it is necessary to be included.
iconSqlString is in the format of (32, 126, 68). The string has over 150 items and is different each time. I need to return the IconUrl for all items in the string. The current query for some reason only returns on IconUrl, for the item with the value of 35 in the 51st position (out of 196). I know that the iconString is in the format of a string, but it is only for testing purposes, I was expecting it to have returned all the results, separated by commas or something else. I wish to return all the values possible to an array, to be called later with something like sqlItem[5]. How would I do this?
ExecuteScalar is for single result, you will need to use ExecuteReader instead.
Also as a side note, you should never use variables to perform a query because of an attack called SQLI or SQL Injection. Here is a link with some examples on how to execute parameterized queries.
I'm currently evaluating Oracle's ODP.NET DataProvider and I ran into a problem that popped up in one of our testcases: When the same command text is executed with different parameter types, the parameter type of the first executed command is used in all following commands.
Take for example the following code:
const int sampleInt32 = 1234567890;
const string sampleNvarchar = "someTestString";
const string sqlCommandtext = "SELECT :PARAM PARAM FROM DUAL";
using (OracleConnection connection = new OracleConnection(builder.ConnectionString))
{
connection.Open();
//Test 1 - Int 32
using (OracleCommand commandInt32 = connection.CreateCommand())
{
commandInt32.CommandText = sqlCommandtext;
commandInt32.Parameters.Add("PARAM", OracleDbType.Int32, sampleInt32, ParameterDirection.Input);
using (IDataReader reader = commandInt32.ExecuteReader())
{
while (reader.Read())
{
int resultInt32 = (int)reader.GetDecimal(0);
Assert.AreEqual(sampleInt32, resultInt32);
}
}
}
//Test 2 - NVarchar
using (OracleCommand commandNVarchar = connection.CreateCommand())
{
commandNVarchar.CommandText = sqlCommandtext;
commandNVarchar.Parameters.Add("PARAM", OracleDbType.NVarchar2, sampleNvarchar, ParameterDirection.Input);
using (IDataReader reader = commandNVarchar.ExecuteReader())
{
while (reader.Read())
{
string resultNVarchar = reader.GetString(0);
Assert.AreEqual(sampleNvarchar, resultNVarchar);
}
}
}
}
If commandInt32 is executed before commandNVarchar, execution of commandNVarchar fails with ORA-01722 - Invalid number. If the order is switched so commandNVarchar is executed first, it fails with "Specified cast is not valid" on reader.GetDecimal.
So far I've tried setting StatementCacheSize=0; Pooling=false; StatementCachePurge=true as ConnectionString parameters but I can't get this to work.
Is there anything I'm missing or are there any other options worth trying?
EDIT: Maybe some background on why this is needed/required: We don't use ODP or any other Dataprovider directly in our application (or at least: we're on our way to reach this goal), there's an DataLayer in between that performs database/provider specific optimiziations and monitoring of connection health,...
In this Layer for example StoredProcedures can be called, having the option of parameter type tuning. Some of our procedures have Clobs as Parameter types, as sometimes the value can be longer than x characters, but most likely it will be shorter.
So before executing via ExecuteNonQuery with ArrayBindCount set to y, parameter values are checked if Clob can be passed as varchar (Nclob as Nvarchar). "Rebinding" reduces the time to execute 2500 records from about 500ms to 200ms at the cost of losing a few ms checking string lengths. And this rebinding can only be done if the parameter type can be changed. Without this option we would need to execute it as Clob everytime, taking the performance hit.
To my understanding, parameter binding is unsupported in a SELECT list. I was so surprised that this worked at all that I had to run your code to see it with my own eyes. I believe that for the client to allow that SQL statement to execute at all is a bug.
Regardless, I inserted the following line between the test cases to get them both to work:
connection.PurgeStatementCache();
However, this only seems to work with the Managed Client (I've tried it with version 4.112.3.60). The regular client still fails as you describe.
Two things. When used as connection string parameters, the configuration variables need to have spaces, ie
Statement Cache Size=0;
The format you are using can be used directly in the config though:
http://docs.oracle.com/html/E10927_01/featConfig.htm#CJABCACG
http://docs.oracle.com/html/E10927_01/featOraCommand.htm#CIHCAFIG
You could use that same configuration section to enable tracing - comparing the traces might give you an idea of what is happening.
I believe PurgeStatementCache (not sure StatementCachePurge exists) is a runtime command, ie
connection.PurgeStatementCache
Metadata Pooling = false;
Our application is using Oracle 12c with ODP.Net Managed Provider
When using OracleCommandBuilder.DeriveParameters() we were always seeing the same parameters return from the stored procedure despite adding/ removing/ updating parameters. We would only see the changes after restarting the IIS process.
The only solution that worked was setting Metadata Pooling = false; in the Oracle connection string
We had no success with the following which have been mentioned here or on Oracle's forums:
connection.PurgeStatementCache();
Statement Cache Size=0;
Pooling = false;
What version of Oracle are you connecting to? This may be a bind variable peaking (or lack thereof) issue. The feature was introduced in 9i but had some issues all the way thru 10. You could try executing the following to see if you can reproduce the problem without ODP.net:
var param varchar2(255)
exec :param:='TEST';
select :param FROM DUAL;
change the type on "param" from varchar2 to number and change the value and reexecute to see what happens.
You could also try executing the command under a different connection instead of a shared one.
In the end, you could simply rename the bind variable in the statement, relative to the type (ie :paramNum or :paramString). The name you give the the parameter on the .net side is irrelevant unless cmd.BindByName is set to true. By default it is false, and variables are bound in the order they are added.
Does ExecuteScalar() have any advantages over ExecuteReader()?
ExecuteScalar only returns the first value from the first row of the dataset. Internal it is treated just like ExecuteReader(), a DataReader is opened, the value is picked and the DataReader gets destroyed afterwards. I also always wondered about that behavior, but it has one advantage: It takes place within the Framework...and you can't compete with the Framework in manners of speed.
Edit By rwwilden:
Taking a look with Reflector inside SqlCommand.ExecuteScalar() you can see these lines:
SqlDataReader ds = this.RunExecuteReader(
CommandBehavior.Default, RunBehavior.ReturnImmediately, true, "ExecuteScalar");
obj2 = this.CompleteExecuteScalar(ds, false);
Exactly what happens inside ExecuteReader. Another advantage is that ExecuteScalar returns null when no data is read. If you use ExecuteReader, you'd have to check this yourself.
From SqlCommand.ExecuteScalar Method
Use the ExecuteScalar method to
retrieve a single value (for example,
an aggregate value) from a database.
This requires less code than using the ExecuteReader method, and then
performing the operations that you
need to generate the single value
using the data returned by a
SqlDataReader.
Also from What is the difference between ExecuteReader, ExecuteNonQuery and ExecuteScalar
ExecuteReader :Use for accessing
data. It provides a forward-only,
read-only, connected recordset.
ExecuteNonQuery :Use for data
manipulation, such as Insert, Update,
Delete.
ExecuteScalar :Use for retriving 1
row 1 col. value., i.e. Single value.
eg: for retriving aggregate function.
It is faster than other ways of
retriving a single value from DB.
From ExecuteScalar page on MSDN:
Use the ExecuteScalar method to retrieve a single value (for example, an aggregate value) from a database. This requires less code than using the ExecuteReader method, and then performing the operations that you need to generate the single value using the data returned by a SqlDataReader
So, it's not faster or better, but is used to reduce the amount of code written when only one value is needed.
When you have a single value returned from your Query or SP it's always better to use ExecuteScalar() as it retrieves the first value of the result. Hence, this is faster in this kind of situation.
Execute Scalar intended to get single value from the database while Execute Reader to get multiple records into DataTable.
ExecuteScalar() will take less resources compared to the ExecuteReader() as later will return the multiple column data from the database.
ExecuteReader() will be instantiating the SqlDataReader which is stream based and query the results from the data source
I've got an ASP.NET 2.0 website that connects to a SQL database. I've upgraded the SQL server from 2000 to 2008 and since then, one page refuses to work.
I've worked out the problem is that the call to SqlDataReader.HasRows is returning false even though the dataset is not empty and removing the check allows the loop through reader.Read() to access the expected data.
_connectionString = WebConfigurationManager.ConnectionStrings["SQLServer"].ConnectionString;
SqlConnection connection = new SqlConnection(_connectionString);
SqlCommand command = new SqlCommand(searchtype, connection);
SqlParameter _parSeachTerm = new SqlParameter("#searchterm", SqlDbType.VarChar, 255);
_parSeachTerm.Value = searchterm;
command.Parameters.Add(_parSeachTerm);
command.CommandType = CommandType.StoredProcedure;
try
{
connection.Open();
SqlDataReader reader = command.ExecuteReader();
if (reader.HasRows) //this always returns false!?
{
while (reader.Read())
{...
Does anybody have any idea what's going on? There are similar code blocks on other pages where HasRows returns the correct value.
EDIT- Just to clarify, the stored procedure DOES return results which I have confirmed because the loop runs through fine if I remove the HasRows check. Changing just the name of the SQL server in the connection string to an identical database running on SQL 2000 makes the problem go away. I've checked that NOCOUNT is off, so what else could make HasRows return false when that's not the case??
EDIT2- Here's the SP
CREATE PROCEDURE StaffEnquirySurnameSearch
#searchterm varchar(255)
AS
SELECT AD.Name, AD.Company, AD.telephoneNumber, AD.manager, CVS.Position, CVS.CompanyArea, CVS.Location, CVS.Title, AD.guid AS guid,
AD.firstname, AD.surname
FROM ADCVS AD
LEFT OUTER JOIN CVS ON
AD.Guid=CVS.Guid
WHERE AD.SurName LIKE #searchterm
ORDER BY AD.Surname, AD.Firstname
GO
Many thanks in advance.
Does the stored procedure work if you invoke it in directly, say in SSMS? I'd start by making sure that it does.
HasRows requires a scrollable cursor.
Do the rows you are bringing back contain any large image/BLOB data?
As someone else suggested, I think posting the Stored Procedure might throw some light on the matter...
First, check the procedure as #tvanfosson says.
Second, the check for HasRows() is actually unnecessary in the code snippet.
You're not using RAISEERROR by chance? We found some problems using the same pattern as above (check HasRows, then reader.Read()) and found that if RAISEERROR was used with a certain error code (above 16, I believe) then the HasRows would return false and we would have problems catching an exception.
It is either your connection string, the stored procedure, or a bug in the sql driver.
Most people are guessing the stored procedure.
So show us the code.
While you are at it, show us the connection string and searchtype variable contents.
I am speculating again.
Do you have multiple datareaders open by any chance?
Add MARS_Connection=yes; OR MultipleActiveResultSets=true to the connection string, if that helps.
Also, your usage of connection & datareader is not a recommended way of doing things
a simpler way to write it could be
using (connection cnn = new Connection(...)
{
using (SqlDataReader rdr = ....
{
//some code which deals with datareader
}
}
This will close the connection and datareader once the operation is complete.
I think you've got NOCOUNT backwards.
I believe NOCOUNT needs to be on for this to work.
In your stored procedure add
SET NOCOUNT ON
after the AS and before any code.
Otherwise it returns two result sets.
One with the count and one with the actual data.
You only want the result set with the actual data.