I'm having trouble with QSqlQuery: it's not updating my SQLite database.
I initialize the database like this (which works fine):
myDB = QSqlDatabase::addDatabase("QSQLITE");
myDB.setDatabaseName(QDir::homePath() + QDir::separator() + "file.db");
myDB.open();
if(!myDB.tables().contains("presets")) {
myDB.exec("CREATE TABLE presets (id INTEGER PRIMARY KEY ASC, path TEXT);");
for (int i=0; i<10; i++)
myDB.exec("INSERT INTO presets (path) VALUES ('n/a')");
}
I have a QString array of values that I want to update into there.
QString presets[10];
preset[0] = "foo";
preset[1] = "bar";
...
I try to update the database with a for loop like this:
QSqlQuery qPreset(myDB);
qPreset.prepare("UPDATE presets SET path=':path' WHERE id=':id';");
for (int i=0; i<10; i++){
qPreset.bindValue(":path", presets[i]);
qPreset.bindValue(":id", i+1);
qPreset.exec();
}
But nothing happens in the database and I don't get any error messages either. Any ideas why?
Edit:
This (ugly as it is) works perfectly fine though, so it's not the connection:
for (int i=0; i<10; i++)
myDB.exec("UPDATE presets SET path='" + presets[i] + "' WHERE id='" + QString::number(i+1) + "';");
Based on the question from #Nejat I have figured out the answer: I had to remove all the single quotes. I failed to notice that QSqlQuery::bindValue() takes care of all the quoting that's necessary.
Working snippet:
qPreset.prepare("UPDATE presets SET path=:path WHERE id=:id;");
Thanks for the hint.
Related
I have a table node={id,name}, and a table segment={id,nodeFrom,nodeTo} in a SQLite db, where node.id and segment.id are AUTOINCREMENT fields.
I'm creating a QSqlTableModel for Node, as follows:
nodeModel = new QSqlTableModel(this,db);
nodeModel->setTable("Node");
nodeModel->setEditStrategy(QSqlTableModel::OnFieldChange);
and I use the following code for inserting nodes:
int addNode(QString name) {
QSqlRecord newRec = nodeModel->record();
newRec.setGenerated("id",false);
newRec.setValue("name",name);
if (not nodeModel->insertRecord(-1,newRec))
qDebug() << nodeModel->lastError();
if (not nodeModel->submit())
qDebug() << nodeModel->lastError();
return nodeModel->query().lastInsertId().toInt();
}
This seems to work. Now, for segments I define a QSqlRelationalTableModel, as follows:
segModel = new QSqlRelationalTableModel(this,db);
segModel->setTable("Segment");
segModel->setEditStrategy(QSqlTableModel::OnManualSubmit);
segModel->setRelation(segModel->fieldIndex("nodeFrom"),
QSqlRelation("Node","id","name"));
segModel->setRelation(segModel->fieldIndex("nodeTo"),
QSqlRelation("Node","id","name"));
And then I have the following code for inserting segments:
int addSegment(int nodeFrom, int nodeTo) {
QSqlRecord newRec = segModel->record();
newRec.setGenerated("id",false);
newRec.setValue(1,nodeFrom);
newRec.setValue(2,nodeTo);
if (not segModel->insertRecord(-1,newRec)) // (*)
qDebug() << segModel->lastError();
if (not segModel->submitAll())
qDebug() << segModel->lastError(); // (*)
}
I can add successfully 280 nodes using addNode(). I can also add segments sucessfully if nodeFrom<=256 and nodeTo<=256. For any segment referencing a node greater or equal to 256 I get a
QSqlError("19", "Unable to fetch row", "Segment.nodeTo may not be NULL")
in one of the lines marked with a (*) of the addSegment function.
I've googled and found out that people are having other (apparently unrelated) problems when they hit the magical 256 record count. No solution seems to work with this particular problem.
What am I doing wrong?
Thanks!
The reason of this error lies in the void QRelation::populateDictionary() method which uses such a loop for (int i=0; i < model->rowCount(); ++i). If you use the database that does not report the size of the query back (e.g. SQLite), the rowCount() method will return this magical 256 value.
You can solve this by populating the relation model before using data(...) or setData(...). At first you can try with:
setRelation(nodeFromCol, QSqlRelation("Node", "id", "name"));
QSqlTableModel *model = relationModel(nodeFromCol);
while(model->canFetchMore())
model->fetchMore();
Try this way to fix
newRec.setValue(1,QVariant(nodeFrom));
newRec.setValue(2,QVariant(nodeTo));
in my program a simple shopping application for my lab exercise, i just calculated the price of items inside a for loop but when i try to print it outside it is not getting printed...pls give me some suggestion.
for (int i = 1; i < 7; i++) {
String selection = request.getParameter("a" + i);
if (selection.equals("l")) {
price = Integer.parseInt(request.getParameter("b" + i));
total = total + price;
out.println("<h3>You have purchased the item:<br>Price is:</h3>" + price);
}
}
out.println("THE TOTAL IS"+total);
out.println("</body>");
out.println("</html>");
String selection = request.getParameter("a" + i);
//Here print the selection there is the problem
if (selection.equals("l")) ///Here alway it is false thats why you are getting this.
{
}
It gets not printed because its out of scope. Your total
variable only lives within the loop.
You have basic programming skills and know about variable scope, right?
http://www.java-made-easy.com/variable-scope.html
I am writing data to a config file using the following code.
QSettings settings("/root/configFile.ini",QSettings::IniFormat);
QString userName = lineEditUsername.text();
QString password = lineEditPassword.text();
QList<QString> listUsername;
QList<QString> listPassword;
settings.beginWriteArray("UserData");
for(i=0;i<listUsername.size();i++)
{
Qstring user = listUsername.at(i);
Qstring pass = listPassword.at(i);
settings.setArryIndex(i);
settings.setValue("Username",user);
settings.setValue("Password",pass);
}
settings.endArray();
}
Now when I run the code first time and give 4 or 5 values they are formed in proper order in the file. However if I run the application for second time the values start overwriting from first position. Can some one suggest me some solution for this?
Instead of creating and maintaining arrays and indexes, I would propose to create user credentials map and store it in the settings file as follows:
QSettings settings("/root/configFile.ini", QSettings::IniFormat);
QString userName = lineEditUsername.text();
QString password = lineEditPassword.text();
QList<QString> listUsername;
QList<QString> listPassword;
//settings.beginWriteArray("UserData");
QVariantMap userDataMapping;
for(int i = 0; i < listUsername.size() ; i++)
{
QString user = listUsername.at(i);
QString pass = listPassword.at(i);
userDataMapping[user] = pass;
//settings.setArryIndex(i);
//settings.setValue("Username",user);
//settings.setValue("Password",pass);
}
// Store the mapping.
settings.setValue("UserData", userDataMapping);
//settings.endArray();
// ...
This will store your data in ini file in the following format:
UserData=#Variant(\0\0\0\b\0\0\0\x1\0\0\0\x6\0\x64\0\x64\0\x64\0\0\0\n\0\0\0\x6\0\x62\0\x62\0\x62)
When you read settings, do something like this:
[..]
QVariant v = settings.value("UserData");
QVariantMap map = v.value<QVariantMap>();
QMapIterator<QString, QVariant> i(map);
while (i.hasNext()) {
i.next();
QString user = i.key();
QString pass = i.value().toString();
}
You need to retrieve the amount of existing entries before adding a new one. Something like this:
int size = settings.beginReadArray( "UserData" );
settings.endArray();
settings.beginWriteArray( "UserData" );
settings.setArrayIndex( size ); // Note: Maybe 'size - 1', not sure
// ...
settings.endArray();
setArrayIndex( size ) will move the array index to the end and will thus no longer override an existing entry
I have a lots of data and I want to insert to DB in the least time. I did some tests. I created a table (using the below script) in PostgreSQL:
CREATE TABLE test_table
(
id serial NOT NULL,
item integer NOT NULL,
count integer NOT NULL,
CONSTRAINT test_table_pkey PRIMARY KEY (id)
)
WITH (
OIDS=FALSE
);
ALTER TABLE test_table OWNER TO postgres;
I wrote test code, created 1000 random values and insert to test_table in two different ways. First, using QSqlQuery::exec()
int insert() {
QSqlDatabase db = QSqlDatabase::addDatabase("QPSQL");
db.setHostName("127.0.0.1");
db.setDatabaseName("TestDB");
db.setUserName("postgres");
db.setPassword("1234");
if (!db.open()) {
qDebug() << "can not open DB";
return -1;
}
QString queryString = QString("INSERT INTO test_table (item, count)"
" VALUES (:item, :count)");
QSqlQuery query;
query.prepare(queryString);
QDateTime start = QDateTime::currentDateTime();
for (int i = 0; i < 1000; i++) {
query.bindValue(":item", qrand());
query.bindValue(":count", qrand());
if (!query.exec()) {
qDebug() << query.lastQuery();
qDebug() << query.lastError();
}
} //end of for i
QDateTime end = QDateTime::currentDateTime();
int diff = start.msecsTo(end);
return diff;
}
Second using QSqlQuery::execBatch:
int batchInsert() {
QSqlDatabase db = QSqlDatabase::addDatabase("QPSQL");
db.setHostName("127.0.0.1");
db.setDatabaseName("TestDB");
db.setUserName("postgres");
db.setPassword("1234");
if (!db.open()) {
qDebug() << "can not open DB";
return -1;
}
QString queryString = QString("INSERT INTO test_table (item, count)"
" VALUES (:item, :count)");
QSqlQuery query;
query.prepare(queryString);
QVariantList itemList;
QVariantList CountList;
QDateTime start = QDateTime::currentDateTime();
for (int i = 0; i < 1000; i++) {
itemList.append(qrand());
CountList.append(qrand());
} //end of for i
query.addBindValue(itemList);
query.addBindValue(CountList);
if (!query.execBatch())
qDebug() << query.lastError();
QDateTime end = QDateTime::currentDateTime();
int diff = start.msecsTo(end);
return diff;
}
I found that there is no difference between them:
int main() {
qDebug() << insert() << batchInsert();
return 1;}
Result:
14270 14663 (milliseconds)
How can I improve it?
In http://doc.qt.io/qt-5/qsqlquery.html#execBatch has been cited:
If the database doesn't support batch executions, the driver will
simulate it using conventional exec() calls.
I'm not sure my DBMS support batch executions or not?
How can I test it?
In not sure what the qt driver does, but PostgreSQL can support running multiple statements in one transaction. Just do it manually instead of trying to use the built in feature of the driver.
Try changing your SQL statement to
BEGIN TRANSACTION;
For every iteration of loop run an insert statement.
INSERT HERE;
Once end of loop happens for all 1000 records issue this. On your same connection.
COMMIT TRANSACTION;
Also 1000 rows is not much to test with, you might want to try 100,000 or more to make sure the qt batch really wasn't helping.
By issuing 1000 insert statements, you have 1000 round trips to the database. This takes quite some time (network and scheduling latency). So try to reduce the number of insert statements!
Let's say you want to:
insert into test_table(item, count) values (1000, 10);
insert into test_table(item, count) values (1001, 20);
insert into test_table(item, count) values (1002, 30);
Transform it into a single query and the query will need less than half of the time:
insert into test_table(item, count) values (1000, 10), (1001, 20), (1002, 30);
In PostgreSQL, there is another way to write it:
insert into test_table(item, count) values (
unnest(array[1000, 1001, 1002])
unnest(array[10, 20, 30]));
My reason for presenting the second way is that you can pass all the content of a big array in a single parameter (tested with in C# with the database driver "Npgsql"):
insert into test_table(item, count) values (unnest(:items), unnest(:counts));
items is a query parameter with the value int[]{100, 1001, 1002}
counts is a query parameter with the value int[]{10, 20, 30}
Today, I have cut down the running time of 10,000 inserts in C# from 80s to 550ms with this technique. It's easy. Furthermore, there is not any hassle with transactions, as a single statement is never split into multiple transactions.
I hope this works with the Qt PostgreSQL driver, too. On the server side, you need PostgreSQL >= 8.4., as older versions do not provide unnest (but there may be work arounds).
You can use QSqlDriver::hasFeature with argument QSqlDriver::BatchOperations
In the 4.8 sources, I found that only oci (oracle) support the BatchOperations. Don't know why not use the COPY statement for postgresql in the psql driver.
I want to add items dynamically to QListWidget in Qt.
I have used the following piece of code to add items dynamically, but I am able to add only one item in QListWidget...
for(int i=0; i<5; i++)
{
structLocationDetails[i].strlocationName = metaresult["locationName"];
QString strtemp = structLocationDetails[i].strlocationName;
list=new QListWidget(this);
list->setSortingEnabled(true);
list->setGeometry(0,0,190, 450);
QStringList items;
item1=new QListWidgetItem(QIcon(":/imagesIcon.png"),structLocationDetails[i].strlocationName,list);
connect(list,SIGNAL(itemClicked(QListWidgetItem*)),this,SLOT(myitem(QListWidgetItem*)));
}
Here actually I am parsing an XML file and trying to add the parsed xml file contents(locationName) to the QListWidget.
How can I achieve this?
Please provide me any suggestions on this...
Thanks...
changing the code do the job...
list=new QListWidget(this);
list->setSortingEnabled(true);
list->setGeometry(0,0,190, 450);
connect(list,SIGNAL(itemClicked(QListWidgetItem*)),this,SLOT(myitem(QListWidgetItem*)));
for(int i=0; i<5; i++)
{
structLocationDetails[i].strlocationName = metaresult["locationName"];
item1=new QListWidgetItem(QIcon(":/imagesIcon.png"),structLocationDetails[i].strlocationName,list);
}