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.
Related
I am trying to take data from six fields on a form and pass the data the user enters into a database (A local SQLite3 database). The ID is an integer and the primary key and the rest are varchars of enough length. After reading a lot of related questions, I still cannot figure out where my code is going wrong. I have confirmation that the program is connected to the database, but I keep getting 'parameter count mismatch'. Where am I going wrong? I'm using Qt 5.15 and C++. The table TOOL exists, as I have it open in SQLite Manager with a test row already inserted.
Below is the function that is supposed to submit the data:
//passes data to database upon clicking submit button
void Add_item::on_submitButton_clicked() {
QString name, location, safety, summary, uses, idNumber;
idNumber = ui->testIdBox->text();
name = ui->nameField->text();
location = ui->locationField->text();
safety = ui->safetyField->text();
summary = ui->summaryField->text();
uses = ui->useField->text();
QSqlQuery qry;
qry.prepare("INSERT INTO TOOLS (TOOL_ID, TOOL_NAME, TOOL_SUMMARY, TOOL_LOCATION, TOOL_USE, TOOL_SAFETY) "
"VALUES (:idNumber, :name, :summary, :location, :uses, :safety)");
//binding all values to prevent sql injection attacks
qry.bindValue(":idNumber", idNumber);
qry.bindValue(":name", name);
qry.bindValue(":summary", summary);
qry.bindValue(":location", location);
qry.bindValue(":uses", uses);
qry.bindValue(":safety", safety);
if(qry.exec()){
QMessageBox::critical(this,tr("Confirmation Message"),tr("Success!"));
}
else {
QMessageBox::critical(this,tr("Confirmation Message"),tr("Error, data was not saved."), qry.lastError().text());
}
connClose();
}
Here is where I connect to the database:
//connecting to database
bool Add_item::connOpen() {
QSqlDatabase mydb = QSqlDatabase::addDatabase("QSQLITE");
mydb.setDatabaseName("C:/Users/laesc/OneDrive/Documents/ToolBuddy/test.db");
if (mydb.open()) {
ui->statusLabel->setText("Connected!");
qDebug()<<("Connected");
return true;
}
else {
ui->statusLabel->setText("Connection Not Successful...");
qDebug()<<("Not Connected");
return false;
}
}
For future viewers, I ended up reducing the number of parameters to 2 and the query worked. For one reason or another the query didn't work if I tried to bind more than 2 variables. I'm just doing multiple queries.
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.
I'm relatively new to QT and C++ and am trying to make a basic front-end for a database.
I've hit a snag where QlineEdits that are left empty or blank write empty data into the database. By that I mean if I select * where 'column'is NULL, I get no results. But If I select * where 'column' = '' then I will get all empty rows.
Is there any way to change this behavior? Or have them written in as NULL? I'm worried it will cause havoc too for fields where I want the database to store QlineEdit data as integers but it will attempt to write in white space.
I could pre-set QlineEdits to 0 i.e.
ui->txt_NAF->setText();
but this will not eliminate human error.
Anyone have a solution for this problem?
Snippet of QT code used to insert into database (SQL server) below;
void log::on_pushButton_clicked() /* this is my save button */
{
MainWindow conn;
QString BANK_MNTH, BUSN_MNGR, CUST_FRST_NAME, CUST_LAST_NAME, DEAL_NUMB ,COST;
BANK_MNTH=ui->txt_BANK_MNTH->text();
BUSN_MNGR=ui->txt_BUSN_MNGR->text();
CUST_FRST_NAME=ui->txt_CUST_FRST_NAME->text();
CUST_LAST_NAME=ui->txt_CUST_LAST_NAME->text();
DEAL_NUMB=ui->txt_DEAL_NUMB->text();
COST=ui->txt_NAF->text();
if(!conn.connOpen()){
qDebug()<<"Failed to open database";
return;
}
conn.connOpen();
QSqlQuery qry;
qry.prepare("insert into LOG (BANK_MNTH, BUSN_MNGR, CUST_FRST_NAME, CUST_LAST_NAME, DEAL_NUMB ,COST) values ('"+BANK_MNTH +"','"+BUSN_MNGR+"','"+CUST_FRST_NAME+"','"+CUST_LAST_NAME+"','"+DEAL_NUMB+"','"+COST+"')");
if(qry.exec( ))
{
QMessageBox::critical(this,tr("Save"),tr("Record Saved"));
QSqlQueryModel * modal=new QSqlQueryModel();
QSqlQuery* qry2=new QSqlQuery(conn.mydb);
qry2->prepare("select DEAL_NUMB, BUSN_MNGR, CUST_FRST_NAME, CUST_LAST_NAME, COST from LOG order by DEAL_NUMB DESC");
qry2->exec();
modal->setQuery(*qry2);
ui->tableView->setModel(modal);
ui->tableView->resizeColumnsToContents();
ui->tableView->setAlternatingRowColors(true);
conn.connClose();
foreach(QLineEdit *log, this->findChildren<QLineEdit*>()) {
log->clear();
}
}
else
{
QMessageBox::critical(this,tr("Error"),qry.lastError().text());
}
}
Yes, you can write them as NULL....
Use place holders for inserting values and pass a null QVariant using below function to add NULL.
void QSqlQuery::bindValue(const QString &placeholder, const QVariant &val, QSql::ParamType paramType = QSql::In)
Your code may look like this ... (Did it for few variables for your idea...)
Write a prepare statement for insert using place holders.
query.prepare("INSERT INTO LOG (BANK_MNTH, BUSN_MNGR, CUST_FRST_NAME) "
"VALUES (:BankMonth, :BusManager, :CustFirstName)");
Now you have BankMonth,BusManager and CustFirstName as place holders of your insert statement.
Now bind the values to place holders as shown below.
Use ternary operator or what ever is convenient to you.
Check if the text is empty, if so pass QVariant(QVariant::String) for NULL else pass the text.
//Some rough code (Not compiled and tested)
BANK_MNTH=ui->txt_BANK_MNTH->text();
BUSN_MNGR=ui->txt_BUSN_MNGR->text();
CUST_FRST_NAME=ui->txt_CUST_FRST_NAME->text();
query.bindValue(":BankMonth", BANK_MNTH.trimmed().isEmpty() ? QVariant(QVariant::String): BANK_MNTH);
query.bindValue(":BusManager", BUSN_MNGR.trimmed().isEmpty() ? QVariant(QVariant::String): BUSN_MNGR);
query.bindValue(":CustFirstName", CUST_FRST_NAME.trimmed().isEmpty() ? QVariant(QVariant::String): CUST_FRST_NAME);
Refer bindValue documentation.
http://doc.qt.io/qt-5/qsqlquery.html#bindValue
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.
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).