tags:

views:

1388

answers:

7

I'm working in Microsoft Visual C# 2008 Express and with SQLite.

I'm querying my database with something like this:

SQLiteCommand cmd = new SQLiteCommand(conn);

cmd.CommandText = "select id from myTable where word = '" + word + "';";
cmd.CommandType = CommandType.Text;
SQLiteDataReader reader = cmd.ExecuteReader();

Then I do something like this:

if (reader.HasRows == true) {
   while (reader.Read()) {
     // I do stuff here
}
}

What I want to do is count the number of rows before I do "reader.Read()" since the number returned will affect what I want/need to do. I know I can add a count within the while statement, but I really need to know the count before.

Any suggestions?

Thanks!

-Adeena

+3  A: 

Do a second query:

cmd.CommandText = "select count(id) from myTable where word = '" + word + "';";
cmd.CommandType = CommandType.Text;
SQLiteDataReader reader = cmd.ExecuteReader();

Your reader will then contain a single row with one column containing the number of rows in the result set. The count will have been performed on the server, so it should be nicely quick.

Jeremy McGee
Be careful about counting the single field: If it is null, it won't be counted, but will be returned. Make sure a field has a NOT NULL constraint on it before using it in a count.
Eric
For a count, you should rather use ExecuteScalar than ExecuteReader...
Thomas Levesque
Absolutely. Generally, I'd hope an field called "id" is NOT NULL, but you can never be sure!
Jeremy McGee
+1  A: 

If you are only loading an id column from the database, would it not be easier to simply load into a List<string> and then work from there in memory?

jerryjvl
If you consider the situation where you have a few hundred thousand rows in the table, this would probably not be what you want to do. Better to run another query ahead of time to find the count of the rowset.
Jeremy McGee
It depends on what the 'I do stuff here' is... depending on the algorithm most/all of the data may end up in memory anyway.
jerryjvl
Also note that in another comment the submitter indicates that typically something like 10 rows is expected from the query, so using a list will probably be faster than two round-trips to the database.
jerryjvl
+5  A: 

The DataReader runs lazily, so it doesn't pick up the entirety of the rowset before beginning. This leaves you with two choices:

  1. Iterate through and count
  2. Count in the SQL statement.

Because I'm more of a SQL guy, I'll do the count in the SQL statement:

cmd.CommandText = "select count(id) from myTable where word = '" + word + "';";
cmd.CommandType = CommandType.Text;
int RowCount = 0;

RowCount = Convert.ToInt32(cmd.ExecuteScalar());

cmd.CommandText = "select id from myTable where word = '" + word + "';";
SQLiteDataReader reader = cmd.ExecuteReader();

//...

Note how I counted *, not id in the beginning. This is because count(id) will ignore id's, while count(*) will only ignore completely null rows. If you have no null id's, then use count(id) (it's a tad bit faster, depending on your table size).

Update: Changed to ExecuteScalar, and also count(id) based on comments.

Eric
yeah - between your response and Jeremy's this will work. While "myTable" will be VERY large eventually, the number of rows returned will likely always be small - less than say 10, more often than not it will only be 0-2. Thanks!
adeena
oh - and "id" will never be null. If it is, something else is wrong. :)
adeena
How big is "VERY large," and are you sure SQLite is the best thing for that?
Eric
For the moment, as I'm learning while coding, I'm sure SQlite is the best for now. Will I need to change to some other database solution in the future? It's likely - but I have so much other things to learn and do, that SQLite is currently meeting my needs. That table will be a couple thousand entries in the near term, but when my application is done and I'm using it fully, it should have minimum 60,000 and as much as 100,000
adeena
60,000 - 100,000 isn't much, at all, so you should be peachy keen with that. Best of luck!
Eric
This isn't directly related to the question but I would recommend not using string concatenation to build the sql string, instead use a parametrized query to prevent against possible sql injection.
Venr
+1  A: 

What you request is not feasible -- to quote Igor Tandetnik, my emphasis:

SQLite produces records one by one, on request, every time you call sqlite3_step. It simply doesn't know how many there are going to be, until on some sqlite3_step call it discovers there are no more.

(sqlite3_step is the function in SQLite's C API that the C# interface is calling here for each row in the result).

You could rather do a "SELECT COUNT(*) from myTable where word = '" + word + "';" first, before your "real" query -- that will tell you how many rows you're going to get from the real query.

Alex Martelli
A: 

but I really need to know the count before

Why is that ? this is usually not necessary, if you use adequate in-memory data structures (Dataset, List...). There is probably a way to do what you want that doesn't require to count the rows beforehand.

Thomas Levesque
I'm trying to implement a version of the "graphmaster" defined here: http://www.alicebot.org/documentation/matching.html Basically - if there are 0 or 1 rows, I have a unique answer and no worries. But if there are 2 or more rows, then I have to do something else fancy to determine which one is the right answer... and it depends on the next node in my "graph". That's about as best as I can 'splain in a comment! :)
adeena
You could retrieve all rows in a list or DataSet, then check the number of rows and do whatever you need to do on the in-memory data according to the number of rows. Unless the query is likely to fetch hundreds or thousands of rows, that shouldn't be a problem.
Thomas Levesque
A: 

Normally i would do

select count(1) from myTable where word = '" + word + "';";

to get the result as fast as possible. In the case where id is an int then it won't make much difference. If it was something a bit bigger like a string type then you'll notice a difference over a large dataset.

Reasoning about it count(1) will include the null rows. But i'm prepared to be corrected if i'm wrong about that.

Ed Sykes
A: 

You do have to count with select count... from...

This will make your application slower. However there is an easy way to make your app faster and that way is using parameterized queries.

See here: http://stackoverflow.com/questions/904796/how-do-i-get-around-the-problem-in-sqlite-and-c/926251#926251

(So besides speed parameterized queries have 2 other advantages too.)

tuinstoel