SQL Database Update in C++ QT Not Advancing to Next ID - c++

QSqlQuery query(db);
for (int i = 0; i < graph.GetTotalV(); i++) {
query.prepare("UPDATE stadiums SET teamName=:name, stadiumName=:stad, capacity=:cap, "
"location=:loc, conference=:con, surfaceType=:sur, roofType=:roof, starName=:star WHERE stadiumID = :id");
query.bindValue(":name", "name");
query.bindValue(":stad", "stad");
query.bindValue(":cap", 123);
query.bindValue(":loc", "loc");
query.bindValue(":con", "con");
query.bindValue(":sur", "sur");
query.bindValue(":roof", "roof");
query.bindValue(":star", "star");
query.bindValue(":id", i);
query.exec();
}
This is my code that involves a loop to update all values in my database to, as of right now, these new values. However, when I run the code, only the values that are aligned with stadiumID = 0 are updated. When I manually change the last line to "WHERE stadiumID = 1", it does nothing instead, as if there was no other rows beyond index 0. What could be causing this issue?
Thanks.

I don't think.., but try to remove spaces.
stadiumID = :id
to
stadiumID=:id

Okay, I figured it out. In SQLite, I forgot to uncheck "make unique" for the team name and star player values. That was why it simply disregarded them when I tried to insert the same values.

Related

Qt error: QSqlQuery::value: not positioned on a valid record when trying to retrive a stat from a table (QComboBox)

this is the code I'm using to get the GoalsFor stat from this table after the user chooses a team from a ComboBox like this using this code:
void MainWindow::on_hometeam_currentIndexChanged(const QString &hometeam)
{
QString hteam(hometeam);
QSqlQuery q("SELECT GoalsForH FROM teams WHERE TEAM=hteam");
q.exec();
int fieldNo = q.record().indexOf("hometeam");
q.next();
qDebug() << q.value(fieldNo).toInt();
}
But this is what the debugger always shows whenever I choose a team:
QSqlQuery::value: not positioned on a valid record
0
I tried everything I came across on the net and it seems like I'm doing exactly what other users or even the documentation say yet to no avail, any help would be appreciated, thanks !
The problem seems to be with the SQL itself; since hteam isn't actually defined in SQL. I would instead recommend using the prepare function, which can also deal with filtering strings to prevent SQL injections. Something like the below should give you the result you are looking for.
void MainWindow::on_hometeam_currentIndexChanged(const QString &hometeam)
{
QString hteam(hometeam);
QSqlQuery q;
q.prepare("SELECT GoalsForH FROM teams WHERE TEAM=:hteam");
q.bindValue(":hteam", hteam);
if ( !q.exec() ) {
qDebug() << q.lastError();
} else {
int fieldNo = q.record().indexOf("GoalsForH");
while ( q.next() ) {
qDebug() << q.value(fieldNo).toInt();
}
}
}
You were also grabbing indexOf("hometeam"), which isn't actually returned by the query. This then returns -1 which wouldn't be valid. Change this to "GoalsForH" to get the proper column index.

How To Check Database Field is NULL

I have this code snippet that making a call to a database table, where I was to perform some actions, like allocate a room, is the field HostelName is NULL.
But after trying many methods turns out the NULL fields are not being truly observed in my code and the execution always enters the first IF statement even though the field is NULL. Is there really a way to check if a field is NULL and perform some actions accordingly? Am a bit of a novice in Qt and Database.
Note:
MatricNo is the primary key. Which obviously the SELECT statement can return just one record(I guess).
qry.prepare("SELECT * from student where matricno='"+matric+"' and HostelName IS NULL");
qry.exec();
QSqlRecord rec = qry.record();
if(rec.isNull("HostelName"))
{
qry.prepare("UPDATE student set HostelName='"+hostelName+"',RoomNo='"+roomNo+"' where MatricNo='"+matric+"'");
if(qry.exec())
{
qry.prepare("UPDATE Rooms set OccupantsNo=OccupantsNo-1 where HostelName='"+hostelName+"' and roomID='"+roomNo+"'");
if(qry.exec())
{
QMessageBox::critical(this,tr("Error"),tr("Student Room Allocated"));
}
else
{
QMessageBox::critical(this,tr("Error"),qry.lastError().text());
}
}
else
{
QMessageBox::critical(this,tr("Error"),qry.lastError().text());
}
}
else
{
QMessageBox::critical(this,tr("Error"),"Cant Allocate Room: \nStudent Room Already Alloted\nTo Re-Allocate, Go to Edit Student Details");//qry.lastError().text());
}
Consider the following problems in your code:
First: your rec is just null! you are assigning QSqlRecord rec immediately after qry.exec() .. where result is currently located before the first record , you have to position query to first record first QSqlQuery::next()
.. at least this is needed to get your code work.
qry.exec();
qry.next();
QSqlRecord rec = qry.record();
Second: your sql statement have already selected based on HostelName IS NULL
but you are checking the result for rec.isNull("HostelName") ... this is just useless and needless, you probably want rec.isNull("RoomNo") or something else or nothing! but after your select statement the record value of HostelName is always NULL.

Qt: Save result of SQL-query in variable, use C++ variable in SQL-query

My project is to program a simple ShopApp. One function is about checking, whether there is enough stock left, so that a customer can buy the desired amount of whatever he wants to buy. The functions looks like (where cart is a std::vector<product> and Pid stands for product id):
bool sqlfunctions::checkStock(){
QSqlQuery query;
int diff, stock;
for(iter cursor = cart.begin();cursor!=cart.end();cursor++){
query.prepare("SELECT stock FROM products WHERE id = cursor->getPid()");
query.exec();
// Need to save result of query into variable stock
stock = ??;
diff = stock - cursor->getAmount;
if(diff < 0){
return false;
}
}
return true;
}
Obviously this function is not functional, because cursor->getPid() is not executed since it is a string. So the question here is how to insert a c++-variable into the sql-query?In regular C++ i would used some swprintf_s function. So is query.prepare(swprintf_s(...)) a good idea?
The second thing is, since both query.exec() and query.prepare() are booleans, which return true or false, depeding on success, how can i store
results of the queries in a c++ variable?
Please note, that I am new to SQL and SQL with Qt. I use QT5.
I already tried to read the documentation about theQSqlQuery class and its functions, BindValue() and addBindValue() seem to be interesting. However i dont really understand how they work.
Edit
So now I've got a minimal example here that does not work yet, despite following the accepted answer. However the compiler does not give me any warnings or errors:
void MainWindow::on_ButtonSQL_clicked()
{
QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL");
db.setHostName("...");
db.setDatabaseName("...");
db.setUserName("...");
db.setPassword("...");
db.setPort(3306);
db.open();
QMessageBox msgBox;
if (db.open()) {
msgBox.setText("It works :)");
msgBox.exec();
}
else {
msgBox.setText("No connection.");
msgBox.exec();
}
QSqlQuery query(db);
// This query worked!
query.exec("INSERT INTO users (id, username, balance) VALUES(25, 'someName', 10000)");
// initialize someNumber to check later, whether it was processed correctly.
int id = 2, someNumber = 20;
query.prepare("SELECT stock FROM products WHERE id = :myid");
query.bindValue(":myid", id);
query.exec();
QString idValue = query.boundValue(0).toString();
someNumber = query.value(0).toInt();
msgBox.setText("The stock is: "+QString::number(someNumber)+"\nThe placeholder has the value: "+idValue);
msgBox.exec();
}
Expected msgBox of the last msgBox is:
The stock is: 100
The placeholder value is: 2
Output actually is:
The stock is: 0
The placeholder value is: 2
If I instead try to select a string (e.g. productName), say with QString myProductName = query.value(0).toString() (and respective changes in the code), the return would be an empty string.
** SOLVED: ** See comment from Floris in the accepted answer. I missed query.next().
It is pretty straight forward actually:
QSqlQuery query;
query.prepare("Select stock from products where id = :input");
query.bindValue(":input", cursor->getPid());
query.exec();
You bind the values to the argument in the string. Arguments follow the format: :name. There is also positional binding which binds in the order it sees ?.
QSqlQuery query;
query.prepare("Select stock from products where id = ?");
// No need for an identifier
query.bindValue(cursor->getPid());
query.exec();
To iterate the records you obtained from a query you can do the following:
QSqlQuery query;
query.prepare("SELECT stock FROM employee WHERE id = ?");
query.bindValue(cursor->getPid());
query.exec();
if (query.next()) {
int stock = query.value(0).toInt();
// You could store the information you obtain here in a vector or something
}
You could also put the prepare statement outside the for loop. If you are interested in iterating multiple records (from a select statement) you can replace the if statement with a while statement.
Concerning QSqlQuery::next():
Retrieves the next record in the result, if available, and positions the query on the retrieved record. Note that the result must be in the active state and isSelect() must return true before calling this function or it will do nothing and return false.
As taken from QSqlQuery. You will need to make this call before the record will actually be accessible with .value(int).

How can fetch the row which has been recently added in the qlistwidget

How to fetch the last inserted item.
An example
QSqlQuery qry;
qry.prepare("SELECT * FROM users");
qry.exec();
while(qry.next()){
ui->listWidget->addItem(qry.value("username").toString());
}
As you seen in the previous example specifically this line ui->listWidget->addItem(qry.value("username").toString());
This line to add every database row in as an item in qlistwidget.
I have tried to use the following but does not work .
QSqlQuery qry;
qry.prepare("SELECT * FROM users");
qry.exec();
while(qry.next()){
ui->listWidget->addItem(qry.value("username").toString());
ui->listWidget->currentItem()->setData(Qt::UserRole, qry.value("id").toString());
}
I want to get last inserted item, to apply the setData() method, on each item was added.
Edit
I have an error, when delete an item Deletes the item that before.
What is the problem in the following code:
ui->listWidget->takeItem(ui->listWidget->row(ui->listWidget->currentItem()));
QSqlQuery qry;
qry.prepare("DELETE FROM users WHERE id=:id");
qry.bindValue(":id",ui->listWidget->currentItem()->data(Qt::UserRole).toString());
if(qry.exec()){
qDebug()<< "Ok";
}else{
qDebug()<< "Errro";
}
You can do next (it is just example) Your code doesn't work because currentItem() return item selected by user or by programmer, but you don't provide selection, so this code doesn't work.
for(int i = 0; i < 10; i++)
{//this code works because
ui->listWidget->addItem(QString::number(i));//here you add item to the end
//here you use last item by count()-1
ui->listWidget->item(ui->listWidget->count()-1)->setData(Qt::UserRole, "id"+QString::number(i));//set data to last item
}
for(int i = 0; i < 10; i++)
{
qDebug() << ui->listWidget->item(i)->data(Qt::UserRole).toString();
}
Output:
"id0"
"id1"
"id2"
"id3"
"id4"
"id5"
"id6"
"id7"
"id8"
"id9"
This code works identical but it is really bad approach
ui->listWidget->setCurrentItem(ui->listWidget->item(ui->listWidget->count()-1));
ui->listWidget->currentItem()->setData(Qt::UserRole, "id"+QString::number(i));
Edit:
First of all takeItem remove item from widget but doesn't delete it from memory. So if you don't want get memory leak you should use delete, gor example
delete ui->listWidget->takeItem(ui->listWidget->row(ui->listWidget->currentItem()));
Back to your problem. Try firstly delete data in database and after that delete it from widget. Something like this
QSqlQuery qry;
qry.prepare("DELETE FROM users WHERE id=:id");
//qDebug() << ui->listWidget->currentItem()->data(Qt::UserRole).toString();//now you can see id which you want to delete
qry.bindValue(":id",ui->listWidget->currentItem()->data(Qt::UserRole).toString());//make sure that margin id in your app is relly margin id in your database
if(qry.exec()){
qDebug()<< "Ok";
}else{
qDebug()<< "Errro";
}
delete ui->listWidget->takeItem(ui->listWidget->row(ui->listWidget->currentItem()));
You should create QListWidgetItem separately and operate on it.
while(qry.next()){
auto *item = new QListWidgetItem(qry.value("username").toString());
item->setData(Qt::UserRole, qry.value("id").toString());
ui->listWidget->addItem(item);
}

QSqlQuery.record() is always empty

I'm editing the database in this manner:
QSqlQuery query;
query.prepare("UPDATE student "
"SET name = ? "
"WHERE id = ?");
QString name = "t";
int id = 3;
query.addBindValue(name);
query.addBindValue(id);
query.exec(); // query exec returns true
QSqlRecord record = query.record(); // but the record is empty!
mTableModel->beforeInsert(record);
The retrieved record is always empty, but the QSqlTableModel still changes! I need the record to be valid because I'm trying to synchronize an sql db with a std::vector.
I'm connecting to the database like this:
mDatabase = QSqlDatabase::addDatabase("QSQLITE");
mDatabase.setDatabaseName("database.db");
mDatabase.open();
I tried calling QSqlQuery::clear(), QSqlQuery::finish() but it didn't help. I also tried to open and close the DB, but it also didn't help. What can I do? :\
Qt is not a pain indeed.
All your code is good. The only wrong assumption is that an update request will automatically give you back the updated record. You have to make a new select request on this id to get the updates data in a QSqlRecord.
//[untested]
QSqlQuery select;
select.prepare("SELECT * from student where id = ?");
select.addBindValue(id);
if (select.exec() && select.next()) {
QSqlRecord record = select.record();
}