How to get count of rows in sqlite table using c in a program? Is it only by running "select * from table" ? and incrementing a variable?
sqlQuery = "select count (*) from company;";
rc = sqlite3_prepare_v2(db, sqlQuery, -1, &stmt, NULL);
printf("prepare status for count : %d\n", rc);
rowcount = sqlite3_step(stmt);
sqlQuery = "select * from company;";
rc = sqlite3_prepare_v2(db, sqlQuery, -1, &stmt, NULL);
printf("prepare status for count : %d\n", rc);
while (SQLITE_ROW == sqlite3_step(stmt)) {
rowcount ++;
}
I am not getting the row count with 1,
But I do get it when I perform 2.
The sqlite3_step() function does not return the results of the query. It returns SQLITE_ROW if there is another row in the result set, or SQLITE_DONE if there isn't. It also returns an error code if something bad happened.
sqlQuery = "select count(*) from company;";
rc = sqlite3_prepare_v2(db, sqlQuery, -1, &stmt, NULL);
if (rc != SQLITE_OK) {
// error handling -> statement not prepared
}
rc = sqlite3_step(stmt);
if (rc != SQLITE_ROW) {
// error handling -> no rows returned, or an error occurred
}
rowcount = sqlite3_column_int(stmt, 0);
To get the values for the current row in the result set, you have to access them by column index, using the prepared statement that is executing the query. Since there is only one column in this query, the index of 0 is used for the left-most, or only, column in the result. And, since the value of that column is an integer, we use the sqlite3_column_int() function, which returns the value as a 32-bit integer.
For most ordinary database operations, you only need to use a few of the functions in the SQLite C API. You should really read and absorb the introduction. It will help you to understand the normal workflow for database operations.
Related
I create multiple tables (at least two):
CREATE TABLE prices0
(
dt NOT NULL PRIMARY KEY,
buy REAL NOT NULL,
sell REAL NOT NULL
) WITHOUT ROWID;
CREATE TABLE prices1
(
dt NOT NULL PRIMARY KEY,
buy REAL NOT NULL,
sell REAL NOT NULL
) WITHOUT ROWID;
and insert statements:
INSERT INTO prices0 (dt, buy, sell) VALUES (?, ?, ?);
INSERT INTO prices0 (dt, buy, sell) VALUES (?, ?, ?);
In my C++ code I open and execute the statements as follows:
for (size_t i = 0; i < 2; ++i)
{
sqlite3_prepare(...);
}
for (size_t i = 0; i < 2; ++i)
{
sqlite3_bind_int64(...);
sqlite3_bind_double(...);
sqlite3_bind_double(...);
sqlite3_step(...);
sqlite3_reset(...);
}
The first call of sqlite3_step function fails with error code '1' and the message 'SQL logic error'.
The same code does not produce any errors with the single table.
it is because when I create the second table the database schema is changed, see
p->zErrMsg = sqlite3DbStrDup(db, "database schema has changed");
in SQLite sources.
I have an sqlite3 database open. I attach a second database, check whether the first and second database have the same value in a table, and then detach the second database. The detach command gives the error "database db2 is locked".
The process is not multi-threaded. There are no other processes accessing the databases. I get the same results on NFS and the local disk. I tried inserting a delay before the detach in case the lock mentioned in the error is related to the file system.
If I comment out the SELECT statement in the code below, the detach works. So basically, attach then detach works, but if I execute SQL code before the detach I receive SQLITE_ERROR return status and the error message shown above.
SQLite version 3.22.0.1
BTW, I realize I could SELECT count(s.id) instead of looping over each row returned from SELECT *, but other posts talk about making sure sqlite3_step() is called until SQLITE_DONE is returned and I wanted to try everything before posting here.
sqlite3_stmt *stmt;
char *errMsg;
const char *zLeftover;
int count = 0;
// DB declared as:
// sqlite3 *DB;
// opened as:
// sqlite3_open(":memory:", &DB);
sqlret = sqlite3_exec(DB, "ATTACH DATABASE foo.db AS db2;", NULL, 0, &errMsg);
assert(sqlret == SQLITE_OK);
sqlret = sqlite3_prepare_v2(DB,
"SELECT * FROM sessions s INNER JOIN db2.sessions s2 ON s2.id=s.id;",
-1, &stmt, &zLeftover);
assert(sqlret == SQLITE_OK);
sqlret = sqlite3_step(stmt);
while (sqlret == SQLITE_ROW) {
++count;
sqlret = sqlite3_step(stmt);
}
sqlret = sqlite3_finalize(stmt);
assert(sqlret == SQLITE_OK);
sqlret = sqlite3_exec(DB, "DETACH DATABASE db2", NULL, 0, &errMsg);
// This assert fails with SQLITE_ERROR
assert(sqlret == SQLITE_OK);
I expect the sqlite3_finalize(stmt) call to remove any internal locking on the attached database. Is there another call or additional SQL that must be executed before the detach? A pragma setting? A configuration option on the lib?
I've searched the usual places with no luck. Thanks in advance for you help.
sqlite> create table test (Qty DECIMAL(10,2));
sqlite> insert into test values(1059.79);
sqlite> insert into test values(-1050);
sqlite> insert into test values(83.58);
sqlite> insert into test values(130.51);
sqlite> insert into test values(-5);
sqlite> insert into test values(-136.68);
sqlite> insert into test values(-2.6);
sqlite> insert into test values(-75);
sqlite> insert into test values(-4.6);
sqlite> select sum(Qty) FROM TEST;
-6.21724893790088e-14
Given that I explicitly specified DECIMAL(10,2) as the field type, why is the sum not 0?
SQLite does not have a DECIMAL type. Math is performed using 64-bit floating point values.
See SQLite3 data types
Rather than messing about, and making my sql incompatible with the other databases I support, I merely wrote a user-defined replacement for the SQLite SUM function, like so:
[SqliteFunctionAttribute(Name = "SUM", Arguments = 1, FuncType = FunctionType.Aggregate)]
class SqliteSum : SqliteFunction {
public override void Step(object[] args, int stepNumber, ref object contextData) {
if (args.Length < 1 || args[0] == null || args[0] == DBNull.Value)
return;
try {
decimal d = Math.Round(Convert.ToDecimal(args[0]), 4);
if (contextData != null) d += (Decimal)contextData;
contextData = d;
} catch (Exception ex) {
WebServer.Log("Exception: {0}", ex);
}
}
public override object Final(object contextData) {
return contextData;
}
}
SQLite does not have decimal type :
NULL
INTEGER
REAL
TEXT
BLOB
So maybe it is converted to Text , then suming happens.
SQLite is powerfull enough to work with, and can rely on for calculations and math, too.
I'm using SQLite. I have two tables:
Log:
pid INTEGER PRIMARY KEY AUTOINCREMENT
action INTEGER NOT NULL
.... ect.
ErrorExtras:
pid INTEGER REFERENCES log(pid)
msg TEXT,
request TEXT
Now when an error occurs, I want to insert into both. Inserting into Log is easy, as it generates the PID, however, inserting into ErrorExtras is harder since I don't know the PID without doing a query. If I insert into Log, query for the PID and then insert into ErrorExtras, that seems quie messy. Is there some kind of shortcut for these kinds of inserts?
In my case, this is possible because the other info in log uniquely identifies the pid (so there's a combined key), but what if this weren't the case?
You needn't query for the insert_id, just use the last_insert_id in the select statement in ErrorExtras and Bob's your uncle.
If you have control of your SQL commands, you can use the last_insert_rowid SQL function like this:
INSERT INTO Log(action) VALUES(42);
INSERT INTO ErrorExtras(pid, msg) VALUES(last_insert_rowid(), 'x');
(But this works only for the very next INSERT command, because afterwards, last_insert_rowid() returns the rowid of the ErrorExtras record.)
If you are using the C API, you can use the sqlite3_last_insert_rowid function:
sqlite3_prepare_v2(db, "INSERT INTO Log(action) VALUES(42)", -1, &stmt, NULL);
sqlite3_step(stmt);
sqlite3_finalize(stmt);
sqlite3_prepare_v2(db, "INSERT INTO ErrorExtras(pid,msg) VALUES(?,?)", -1, &stmt, NULL);
sqlite3_bind_int64(stmt, 1, sqlite3_last_insert_rowid(db));
sqlite3_bind_text(stmt, 2, "x", -1, SQLITE_TRANSIENT);
sqlite3_step(stmt);
sqlite3_finalize(stmt);
The APIs of other languages typically also have some mechanism to get the last inserted rowid.
For example, in Android, the insert function returns it:
ContentValues cv = new ContentValues();
cv.put("action", 42);
long log_rowid = db.insert("Log", null, cv);
ContentValues cv = new ContentValues();
cv.put("pid", log_rowid);
cv.put("msg", "x");
db.insert("ErrorExtras", null, cv);
I have file with multiple SQL statements in it to be executed.
INSERT INTO reports (a,b,c) VALUES (1,2,3);
INSERT INTO units (report_id, e, f, g) VALUES ( (SELECT last_insert_rowid() FROM reports), 4, 5, 6);
INSERT INTO elements (report_id, h, i, j) VALUES ( (SELECT last_insert_rowid() FROM reports), 7, 8, 9);
The FROM reports section of the sub-selection does nothing.
What ends up happening is:
A row is inserted into reports and the reports.id field is autoincremented
A row is inserted into units with report_id being equal to the reports id
A row is inserted into elements with report_id being equal to units.id of the last row inserted
This is works as described in the sqlite documentation.
My issue is that I want all the queries subsequent to the report insert to use report.id.
Is there anyway I can get this to work on the database end without having to resort to a solution in as3?
There is a way to do this, but it is in AS3 using parameters.
What is done is instead of using the SELECT last_insert_row() function in each call, replace it with a parameter.
INSERT INTO elements (report_id, h, i, j) VALUES (#id, 7, 8, 9);
Now in my code I have to split the file into an array so that each individual queries is process separately (this is how AS3 implements sqlite's API).
var sqlArray:Array = sql.split(";\n\n");
Now what I do is execute the first statement for importing the report itself.
statement.text = sqlArray[0];
statement.execute();
Now the fun part. You need to get the id back out. So we run another query.
statement.text = "SELECT last_insert_rowid() as ID";
statement.execute();
var id:int = statement.getResult().data[0].id;
Now we can loop through the rest of the queries using the id as a parameter.
for(var i:int = 1; i < sqlArray.length - 1; i++) {
/**
* start at 1 because we already inserted the report
* end at length -1 because our last entry is empty because of how split works on our data
**/
statement.text = sqlArray[i];
statement.parameters['#ID'] = id;
statement.execute();
}
This is a little more complicated, but not much and it ends up working.
Everything rolled together into a single function (omitting a lot of class overhead) would be:
function importFromSQLString(sql:String):void {
try{
connection.begin();
var sqlArray:Array = sql.split(";\n\n");
statement.text = sqlArray[0];
statement.execute();
statement.text = "SELECT last_insert_rowid() as ID";
statement.execute();
var id:int = statement.getResult().data[0].id;
for(var i:int = 1; i < sqlArray.length - 1; i++) {
statement.text = sqlArray[i];
statement.parameters['#ID'] = id;
statement.execute();
}
connection.commit();
statement.clearParameters();
} catch (e:Error) {
connection.rollback(); //cleanup if there was a failure
}
}