I need to be able to recover the ids of the rows updated by an UPDATE query using the QSqlQuery class of Qt.
I don't know if it is even possible, but there is a similar mecanism which allows to recover the id of the last row inserted by a QSqlQuery: http://doc.qt.io/qt-5/qsqlquery.html#lastInsertId
If anyone can give me a way to do that or to ensure me that it is not possible, it would be great.
Using PostgreSQL (not available with SqlServer):
QSqlQuery query("UPDATE myTable SET value=1 WHERE value=0 RETURNING id");
while (query.next()) {
int id = query.value(0).toInt();
qDebug() << id;
}
Related
when I try to select with QSqlQuery:
select mwst, sum(in), sum(out) from costs group by mwst
I got the error: QSqlQuery::value: not positioned on a valid record
In SQLite Database Browser tool the select runs without error.
Thanks for help!
have you used bool QSqlQuery::next() to set the query on a valid value (if the query succeeded, you should get results
like:
QSqlQuery qry("select * from table");
while (qry.next())
{
qDebug() << qry.value(0).toString();
}
I am trying to get the primary key of an inserted row within a transaction scope, because I do not want to leave the db in a logically inconsistent state.
My problem is I cannot find a way to retrieve the ID value of a previously executed query, which I want to use for the next insert query. Querying the PostgreSQL database while the transaction is in effect shows no results in the non-foreign-key table(the row is not yet committed?). I believe this is due to the transaction's isolation level.
Below is what I'm trying to do with production code, albeit slightly edited and narrowed down to one function for clarity. const int lastInsertId is always 0, which in this context should mean no value was found (technically that toInt() function failed). I tried manually inserting a valid non-foreign-key row, and then calling LASTVAL() which produced the expected result - the ID of the inserted row.
So, what am I doing wrong? What am I missing or misunderstanding here?
void createEntityWithoutForiegnKeyConstraint(const QString &nameOfEntity)
{
db_.transaction();
QSqlQuery insertQuery(db_);
insertQuery.prepare("INSERT INTO \"EntityWithoutForeignKey\" (\"name\") VALUES (:name);");
insertQuery.bindValue(":name", nameOfEntity);
execQuery(__LINE__, insertQuery);
QSqlQuery lastIdQuery("SELECT LASTVAL();", db_); // auto executes
const int lastInsertId = lastIdQuery.value(0).toInt();
if (lastInsertId <= 0) // 0 is not a valid ID
throw exception("Oh noes.");
createEntityWithForeignKeyConstraint(lastInsertId, someData);
if (!db_.commit())
db_.rollback();
}
I realise this is an old question but in Qt 5.10 (and likely earlier) there is a function QSqlQuery::lastInsertId() which can be called after QSqlQuery::exec().
It's quite useful if you are using a database such as SQLite which doesn't support the RETURNING clause on an INSERT statement.
QSqlQuery::lastInsertId() documentation.
Usage is something along the lines of the following:
QSqlQuery q;
q.prepare("INSERT INTO table_name VALUES(:some_column_name)");
q.bindValue(":some_column_name", "FooBar");
q.exec();
qDebug() << "Last ID was:" << q.lastInsertId();
#Kasheen is perfectly right but I want to set the focus on one important aspect you should keep in mind: Don't forget to encapsulate everything in one single transaction.
Why? It saves time and avoids database corruption if the second insert using the generated primary key (you got with q.lastInsertId()) fails.
So your insert should look like this (this is basically #Kasheen's answer with some additions):
db_.transaction(); // Starts a transaction
QSqlQuery q;
// first insert
q.prepare("INSERT INTO table_name VALUES(:some_column_name)");
q.bindValue(":some_column_name", "FooBar");
q.exec();
auto pk == q.lastInsertId().toInt;
// second insert
q.prepare("INSERT INTO other_table VALUES(:other_column_name, :fk)");
q.bindValue(":other_column_name", "OtherText");
q.bindValue(":fk", pk);
q.exec();
db_.commit(); // Commits transaction
#average joe does the transaction handling correctly but you might forget it if you look on the solution only.
Hi
I have a sqlite db which I am manipulating using qts built in sqlite database driver.
I have a small test app that allows me to run an sql query from a line edit and it will be executed and the results are then updated in a view of the relevant model.
I have created a table which uses autoincremented primary key values, but if I execute an insert statement without providing the key, I get two rows inserted, each with an autoincremented value.
If I provide the key value, only one row is created. Any ideas why this is?
Table is simple enough, e.g
CREATE TABLE GroupNames ( ID integer PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE, Name varchar(50))
and when I run the query
insert into groupnames (name) values ("testName");
I get two new rows with autoincremented ids. However, if I run
insert into groupnames (id, name) values (100, "testName");
I get one row as expected, with the correct id 100.
Also of note is that if I try
insert into table groupnames (id, name) values (100, "testName");
insert into table groupnames (name) values ("testName");
the query does not run.
The qt code to run the query could not be simpler:
QSqlQuery *DbCore::run_query(const QString &query_string)
{
QSqlDatabase db = QSqlDatabase::database(defConnectionName);
if(!db.isOpen())
return NULL;
QSqlQuery *q = new QSqlQuery(query_string, db);
q->exec();
return q;
}
I have added some logging code to check that the query is executed once:
QSqlDatabase db = QSqlDatabase::database(defConnectionName);
if(!db.isOpen())
return NULL;
qDebug() << "Running query:" << query_string;
QSqlQuery *q = new QSqlQuery(query_string, db);
if(!q->exec())
qDebug() << "Error running query:" << q->lastError();
return q;
The log confirms that I'm only executing once:
Running query: "insert into groupnames (name) values ("hello")"
If i then check the database using sqlite3 shell (to remove any doubt about qt views etc):
sqlite> select * from groupnames;
1|hello
2|hello
question was answered above in a comment:
As i see in the documentation, when you create a QSqlQuery the way you do, the query, if not empty, is executed. To create the QSqlQuery and execute the query, use this: QSqlQuery *q = new QSqlQuery(db); q->exec(query_string) To see the last executed query, use QSqlQuery::lastQuery() And for the last query that was successfully executed QSqlQuery::executedQuery() Hope this helps. – Hector Mar 16 at
I've made a button that removes all the objects in my array, which is shown in my tableView. Then it reloads data and the tableview is empty. But how can I delete all the data from my SQLite database as well, and not just the array? Now the data occurs when I restart. I have tried this:
Button void:
- (void) deleteAllButton_Clicked:(id)sender {
[appDelegate removeAllBooks:nil];
[self.tableView reloadData];
}
Appdelegate void:
-(void) removeAllBooks:(Book *)bookObj {
//Delete it from the database.
[bookObj deleteAllBooks];
//Remove it from the array.
[bookArray removeAllObjects];
}
Delete syntax in Book.m
- (void) deleteAllBooks {
if(deleteStmt == nil) {
const char *sql = "WHAT DO I HAVE TO DO TO DELETE ALL THE ROWS?";
if(sqlite3_prepare_v2(database, sql, -1, &deleteStmt, NULL) != SQLITE_OK)
NSAssert1(0, #"Error while creating delete statement. '%s'", sqlite3_errmsg(database));
}
//When binding parameters, index starts from 1 and not zero.
sqlite3_bind_int(deleteStmt, 1, bookID);
if (SQLITE_DONE != sqlite3_step(deleteStmt))
NSAssert1(0, #"Error while deleting. '%s'", sqlite3_errmsg(database));
sqlite3_reset(deleteStmt);
}
Well, the normal SQL syntax would be:
DELETE FROM tablename
If you have a standard primary id column (and you should) you can do
DELETE FROM tablename WHERE id > -1;
And this will delete all rows in the table since there is no id less than 0.
DELETE FROM tablename did not work for me. I was using a sqlite3 database on iPhone, which I assume the poster was, as well. For me to get this to work, I needed to:
DROP table tablename
followed by a CREATE statement, if I wanted the table to still exist (without any rows).
Of course, that requires knowing the right CREATE statement. Using the SQLManager plugin for Firefox quickly reverse-engineered for me the correct CREATE statement to use. I never figured out why DELETE FROM tablename didn't work, but it definitely didn't, in my case.
I currently have this code:
// Construct query
QString const statement = QString("drop database if exists %1")
.arg(databaseName_);
QSqlQuery query(db);
query.exec(statement);
Is there a better way to code than the above?
Specifically, I dont like how I use QString for SQL statement. It'd be nice if Qt has some class so that I could do something like:
// Construct query
QSomeClass statement = "drop database if exists %1";
statement.setArg(1, databaseName_); // Replace all %1 in the original string.
QSqlQuery query(db);
query.exec(statement);
I think you are basically describing query placeholders:
QSqlQuery query;
query.prepare("INSERT INTO person (id, forename, surname) "
"VALUES (:id, :forename, :surname)");
query.bindValue(":id", 1001);
query.bindValue(":forename", "Bart");
query.bindValue(":surname", "Simpson");
query.exec();
The only difference between the code snipped above and QSomeClass you described is the fact that you have to specify database when creating the query.