Invalid use of group function Error in MariaDB - Update Query and SUM - sql

So im trying to write an update query that either updates the funds of a user based on if they have visa or MasterCard.
What im trying to state here, and which im thinking is what causes the error, is to update the sum if the current balance and wanted withdraw amount is less than 10.000. If that is not the case and the visa balance becomes less than 0, im creating an intentional error so that i can use it later to redirect the user to an error page (i know its not the most PC way to do it).
This is how the code looks:
const connection = require('../models/loginrouters');
function takeMoney(amount, cardnumber) {
// prettier-ignore
console.log("db cardnumber is".cardnumber)
console.log('db amount is', amount);
connection.query(
"UPDATE users.usercards SET Balance = CASE WHEN type = 'visa' AND balance>'" +
amount +
"' THEN Balance - '" +
amount +
"' ELSE CASE WHEN type='mastercard' AND SUM(balance - '" +
amount +
"')<'-10000' THEN Balance - '" +
amount +
"' ELSE 'NEIN CASH' END END WHERE CardNumber = '" +
cardnumber +
"';",
function(err) {
if (err) {
console.log('You too poor');
console.log(err);
} else {
console.log('You got the cash');
}
}
);
}
module.exports = takeMoney;
When this query is run I get the following error:
Error: ER_INVALID_GROUP_FUNC_USE: Invalid use of group function
The query is posted to me as:
sql: 'UPDATE users.usercards SET Balance =
CASE WHEN type = \'visa\' AND balance>\'1000\'
THEN Balance - \'1000\'
ELSE
CASE WHEN type=\'mastercard\' AND SUM(balance - \'1000\')<\'-10000\'
THEN Balance - \'1000\'
ELSE \'NEIN CASH\'
END
END
WHERE CardNumber = \'123456\';'
In advance, thanks for your response!

So as Gordon Linoff stated in a comment, I cannot use SUM in an update query. I just wrapped it in a () and it works perfectly.
TLDR; DONT use SUM in an update query.
connection.query(
"UPDATE users.usercards SET Balance = CASE WHEN type = 'visa' AND balance>'" +
amount +
"' THEN Balance - '" +
amount +
"' ELSE CASE WHEN type='mastercard' AND (balance - '" +
amount +
"')>'-10000' THEN Balance - '" +
amount +
"' ELSE 'NEIN CASH' END END WHERE CardNumber = '" +
cardnumber +
"';",
function(err) {
if (err) {
console.log('You too poor');
} else {
console.log('You got the cash');
}
}
);
}

Related

How to find out the exceptions will be thrown in method?

I have this code and this exception handling. How to find out all possible exceptions will be thrown and handle it? my handling isn't correct? I think about SQLException, NullPointer- etc. I need to find the maximum number by year from 3 tables. Which exceptions can be?
public Integer getMaxNumberByYear(String year){
String queryStr = "SELECT MAX(trip_card_number)\n" +
" FROM (SELECT trip_card_number FROM employee_trip_cards\n" +
" WHERE EXTRACT(YEAR FROM card_Issue_Date) = '" + year + "'\n" +
" UNION ALL\n" +
" SELECT trip_card_number FROM postgraduate_trip_cards\n" +
" WHERE EXTRACT(YEAR FROM card_Issue_Date) = '" + year + "'\n" +
" UNION ALL\n" +
" SELECT trip_card_number FROM students_trip_cards \n" +
" WHERE EXTRACT(YEAR FROM card_Issue_Date) = '" + year + "'\n" +
" )";
try {
Integer result = ((BigDecimal) getSessionFactory().getCurrentSession().createSQLQuery(queryStr).uniqueResult()).intValue();
if (result==null){
throw new NullPointerException("Can't find the maximum");
}
return result;
} catch (NullPointerException e){
e.printStackTrace();
Utils.getSystemLogger().log(e);
return 0;
}
}
First, there is no need to throw a NullPointerException and then catch it in the same method. What you have written looks like handling logic with exceptions, which is not good. Consider writing:
if (result==null){
Utils.getSystemLogger().log("Can't find the maximum");
return 0;
}
return result;
if 0 is an allowed return value for the method, or do not catch the exception otherwise.
You do not have to think ahead and find all the possible exceptions. If you do not know the possible exceptions yet, you also do not know how to recover from them, thus it is better to let them be thrown and debug later.

Geode/Gemfire OQL's return data is incorrect in transaction view

Hello, I inserted a new entry to a region, and queried an OQL (SELECT * FROM /some_region_name) both within the same transaction, and I found that the new inserted entry was not returned in the OQL result. Dose anyone know what was going on with it?
Here are my testing codes:
ClientCache cache = (ClientCache) EcnSpringContext.getBean("gemfireCache");
Region<String, RUserEntity> userRegion = (Region<String, RUserEntity>) EcnSpringContext.getBean("r_user");
CacheTransactionManager txmgr = cache.getCacheTransactionManager();
EcnGeodeTemplate ecnGeodeTemplate = (EcnGeodeTemplate) EcnSpringContext.getBean("rUserTemplate");
String username = "forrest";
System.out.println("Transaction begin.");
txmgr.begin();
System.out.println("checking username[" + username + "] before added.");
RUserEntity found = (RUserEntity) userRegion.selectValue("SELECT * FROM /r_user WHERE username = '" + username + "'");
if (found == null)
System.out.println("rUserEntity NOT found");
else
System.out.println("rUserEntity found");
RUserEntity rUserEntity = new RUserEntity();
rUserEntity.setUsername("forrest");
rUserEntity.setId(username);
userRegion.put(username, rUserEntity);
System.out.println("checking username[" + username + "] after added.");
found = (RUserEntity) userRegion.selectValue("SELECT * FROM /r_user WHERE username = '" + username + "'");
if (found == null)
System.out.println("rUserEntity NOT found");
else
System.out.println("rUserEntity found");
txmgr.commit();
System.out.println("Transaction end.");
System.out.println("checking username[" + username + "] after transaction.");
found = (RUserEntity) userRegion.selectValue("SELECT * FROM /r_user WHERE username = '" + username + "'");
if (found == null)
System.out.println("rUserEntity NOT found");
else
System.out.println("rUserEntity found");
I expected the result:
Transaction begin.
checking username[forrest] before added.
rUserEntity NOT found
checking username[forrest] after added.
**rUserEntity found**
Transaction end.
checking username[forrest] after transaction.
rUserEntity found
But unfortunately I got this result:
Transaction begin.
checking username[forrest] before added.
rUserEntity NOT found
checking username[forrest] after added.
**rUserEntity NOT found**
Transaction end.
checking username[forrest] after transaction.
rUserEntity found
This is a known limitation. From the docs:
Queries and indexes reflect the cache contents and ignore the changes
made by ongoing transactions. If you do a query from inside a transaction,
the query does not reflect the changes made inside that transaction.

SQL Isolation level

We are using session isolation serializable in our application. The intended behavior is that when a user is going insert a new row, it should-should check for the presence of the row with the same key and update the same if row found. But I have found multiple rows created for the same key in SQL server. Is this issue with isolation or the way we are handling the case?
Following is the code I am using,
private int getNextNumber(String objectName, Connection sqlConnection) throws SQLException {
// TODO Auto-generated method stub
int number = 0;
try{
sqlConnection.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
System.out.println("##### Transaction isolation set : " + sqlConnection.getTransactionIsolation());
Statement stmt = sqlConnection.createStatement();
ResultSet rs = stmt.executeQuery("select * from [dbo].[db] where DocumentNumber = '" + objectName.toString() + "' FOR UPDATE");
while(rs.next()) {
printNumber = rs.getInt("PrintNumber");
}
System.out.println("#### Print number found from sql is : " + printNumber);
if(printNumber == 0) {
printNumber = printNumber + 1;
stmt.execute("INSERT INTO [dbo].[db] (number, DocumentNumber) VALUES (1 ,'" + objectName.toString() + "')");
} else {
number = number + 1;
stmt.execute("UPDATE [dbo].[db] SET Number =" + number + " WHERE DocumentNumber ='" + objectName.toString() + "'");
}
//sqlConnection.commit();
}catch(Exception e) {
sqlConnection.rollback();
e.printStackTrace();
} finally {
sqlConnection.commit();
}
return number;
}
Thanks,
Kishor Koli
It's an issue with the way your database is set up. You need a unique constraint to enforce uniqueness. You can check at insert time all you like but a unique constraint is the only way it's going to work 100% so it's just a waste of time selecting before inserting in the hope you'll prevent a duplicate. Insert, catch the exception/error or proceed.

PL/SQL - Aggregate values & Write to table

I am just starting to dabble in PL/SQL so this question may be very straightforward. Here is the scenario:
I have several checkboxes which carry a weighted numeric value. For example:
Checkbox I --> Value '5'
Checkbox II --> Value '10'
Checkbox III --> Value '15'
etc.
The form would have 15 checkboxes in total and the end-user can select anywhere from 0 to all 15. As they select the checkboxes, the total weight would get calculated and a final numeric value would be displayed. For example. checking off 3 Checkbox I & 2 Checkbox III would = 45 points.
Now the total value of 45 would equal to a separate value. Example:
At 0 points, value = 'Okay'
1-15 points, value = 'Error'
16-30 points, value = 'Warning'
31+ points, value = 'Critical'
The form itself is built within Oracle APEX and I can do it using Dynamic Actions but using PL/SQL may be a better solution.
In summary, I'd like the hidden field to first calculate the total from the checked checkboxes and then use that total to figure out the value of either Okay, Error, Warning, or Critical.
Any assistance is much appreciated!
In my experience, it is better if we're going to use javascript on your case since we have to manipulate DOM and events of the checkboxes. If you want to display/change item values and get values at runtime, then javascript is better in doing that than PLSQL unless you want to submit your page every time you check/uncheck a box in your page which is not advisable.
Here is my solution for your question.
First, create a Display Only item on your page. This is where the values "Okay", "Error", "Warning", or "Critical" will appear. And its very important to set it's default value to 0. Then inside your page's "Function and Global Declaration" part, put the following functions:
function getcheck(checkbox_id,displayOnly_id){
var chkboxName = document.getElementById(checkbox_id + "_0").getAttribute("name");
var chks = document.getElementsByName(chkboxName);
var howmanychecks = chks.length;
var currentSum=0;
var v_remarks = "";
for(x=0;x<howmanychecks;x++){
chks[x].setAttribute("onchange","checkIfchecked(this,\'" + displayOnly_id + "\')");
if(chks[x].checked){
currentSum = currentSum + Number(chks[x].value);
}
}
if(currentSum==0){
v_remarks = "Okay";
}
else if(currentSum>0 && currentSum<=15){
v_remarks = "Error";
}
else if(currentSum>15 && currentSum<=30){
v_remarks = "Warning";
}
else{
v_remarks = "Critical";
}
document.getElementById(displayOnly_id).value = currentSum;
document.getElementById(displayOnly_id + "_DISPLAY").innerHTML = currentSum + ": " + v_remarks;
}
function checkIfchecked(p_element, displayOnly_id){
var v_difference;
var v_sum = Number($v(displayOnly_id));
var displayOnly_display = displayOnly_id + "_DISPLAY";
var v_remarks = "";
if(p_element.checked){
v_sum = v_sum + Number(p_element.value);
$("#" + displayOnly_id).val(v_sum);
}
else{
v_difference=Number($("#" + displayOnly_id).val())-Number(p_element.value);
if(v_difference<0){
v_difference=0;
}
$("#" + displayOnly_id).val(v_difference);
}
if($("#" + displayOnly_id).val()==0){
v_remarks = "Okay";
}
else if($("#" + displayOnly_id).val()>0 && $("#" + displayOnly_id).val()<=15){
v_remarks = "Error";
}
else if($("#" + displayOnly_id).val()>15 && $("#" + displayOnly_id).val()<=30){
v_remarks = "Warning";
}
else{
v_remarks = "Critical";
}
document.getElementById(displayOnly_display).innerHTML=$("#" + displayOnly_id).val() + ": " + v_remarks;
}
The above functions will get the sum of the values of those boxes that are checked. A value of a box will be taken out of the current sum if it is unchecked as well. It will also display the remarks for the current checked points whether if it is "Okay", "Error", "Warning", or "Critical".
In your "Execute when Page Loads" part of your page, add the following line:
getcheck(nameofyourcheckboxitem,nameofyourdisplayonlyitem);
where nameofyourcheckboxitem is the name of your Check Box and nameofyourdisplayonlyitem is the name of the Display Only item you have just created.
Here's a sample line on how to use the function that I've given you:
getcheck("P1_MYCHECKBOX","P1_MYDISPLAYONLY");

query.next() returning false

I have used the query.next() function inside my code, but its returning false.
I even checked in the database, there are 4 records present. But the code shows only one.
However if i use query.next() before the code of query.valid() then it doesn't show any record
Please help
qDebug() << "entering payment: get all the unchecked invoices for user: " + user;
QStringList tmp;
QSqlQuery query(m_storageUserManager->database());
m_storageUserManager->dumpTable(m_invoiceInfoTable);
m_storageUserManager->dumpTable(m_invoiceUserTable);
qDebug()<<"THE NAME OF THE INVOICE USER TABLE_----=-----------------"<<m_invoiceInfoTable;
qDebug()<<"THE NAME OF THE INVOICE USER TABLE_----=-----------------"<<m_invoiceUserTable;
query.prepare("SELECT invoice FROM "+ m_invoiceInfoTable +" WHERE invoice = (SELECT
invoice FROM "+ m_invoiceUserTable +" WHERE user=:user)");
// query.prepare("SELECT invoice FROM " + m_invoiceInfoTable + ","+ m_invoiceUserTable +" WHERE " + m_invoiceInfoTable + ".user = " + m_invoiceUserTable + ".:user");
query.bindValue(":user", user);
query.exec();
query.first();
qDebug()<<"Unchecked invoices done!!! " ;
if(query.isValid()) {
do {
tmp.append(query.value(0).toString()); //as the value returned by value() is a QVariant so we need to change it to String.
} while(query.next());
} else
tmp.append("No Unchecked invoice in the database");
return tmp;
To check if the query was successful you should test the return value of either QSqlQuery::exec() or QSqlQuery::isActive() before trying to call first/next/last (when you pass the query string to the constructor of QSqlQuery, the query is already executed, so, you need to use QSqlQuery::isActive()).
first(), next() and last() return true if they positioned the query on a valid record, you don't have to test isValid() separately. Since first() is a positioning function too, you can read the value without calling next() directly after, unless you want to skip the first record.
Since you may want to add fields from the the "invoice-info" table to your query, I kept the subquery (with IN instead of = as Mat already answered in the comment).
query.prepare(QString("SELECT invoice FROM %1 WHERE invoice "
"IN (SELECT invoice FROM %2 WHERE user=:user)")
.arg(m_invoiceInfoTable, m_invoiceUserTable));
/*
// Or with an inner join
query.prepare(QString("SELECT %1.invoice FROM %1 "
"INNER JOIN %2 ON %1.invoice = %2.invoice "
"WHERE user=:user").arg(m_invoiceInfoTable, m_invoiceUserTable));*/
query.bindValue(":user", user);
if (!query.exec()) {
tmp.append("Query error: %1" + query.lastError().text());
} else if (!query.first()) {
tmp.append("No Unchecked invoice in the database");
} else {
do {
tmp.append(query.value(0).toString());
} while(query.next());
}
Try
query.prepare("SELECT invoice FROM " + m_invoiceInfoTable + " WHERE user=:user");