Division by zero handled differently - mysql

I am not sure if this has been answered before in S.O, but I couldn't find any answers googling to explain this behaviour in various databases. so I thought I would clarify this here from experts.
When I run this query in Oracle, Postgresql, mysql and sql-server, I get varying results.
Oracle 11g
select count(1/0) from dual;
ORA-01476: divisor is equal to zero
http://sqlfiddle.com/#!4/e0ee9e/751
SQL-Server 2017
select count(1/0) ;
1
but select 1/0 gives error.
Divide by zero error encountered
http://sqlfiddle.com/#!18/2be41/2
http://sqlfiddle.com/#!18/185a6/7
PostgreSQL 9.6
ERROR: division by zero
http://sqlfiddle.com/#!17/340e0/80
Mysql 5.6
select count(1/0) ;
0
http://sqlfiddle.com/#!9/2be41/10
Why these differences in implementations?

count() counts the number of rows or the non-NULL values of the expression.
When the expression is constant, then it returns the number of rows.
What you are seeing is the question of when and whether the constant is evaluated, and the different way that the databases handle this situation.
Oracle and Postgres are clearly trying to evaluate the constant. To be honest, I'm not sure if these are run-time or compile-time errors.
SQL Server postpones the evaluation until needed -- and it is never needed. So, it counts the number of rows.
MySQL is the strange one. It returns NULL for divide-by-zero. This is not standard behavior, but also not entirely unreasonable. COUNT(NULL) returns 0.

In mysql there is a specific mode (enabled or disable)
ERROR_FOR_DIVISION_BY_ZERO
that manage this situation and in your case seems
disable (you should check for proper mode value) so you apparently see a wrong (or unexpected result) but is simply a configuration for error handling
https://dev.mysql.com/doc/refman/5.7/en/precision-math-expressions.html

Related

User-Defined Variable Assignment difference between MySQL and MariaDB

Of course, we do our job before and searched this community or the web for similar posts but we found nothing.
Issue:
We are not happy with our current hosting provider and have to make change. In the middle of our database switch from MySQL 5.7 to MariaDB 10.3, we have recognized a SET handling difference, which works perfect in MySQL.
SQL code:
SET #sb = 0;
SELECT art,sb
FROM ARTICLE
WHERE(#sb:=sb) > 700 AND #sb <= 1000 >AND art = 'AM';
MySQL result:
art
sb
AM
900.00
AM
960.00
AM
1000.00
AM
770.00
AM
800.00
the list is much longer...
MariaDB result:
art
sb
AM
770.00
AM
960.00
AM
1200.00
AM
3000.00
only 10 rows...
In fact there is a difference between them.
But we want to know:
Can we configure this in MariaDB, that we get the same result in MySQL?
Should we replace the Set handling in MariaDB with another one?
No, this is not possible.
In fact, the MySQL documentation warns about the use of variable assignments inside SQL statements:
The order of evaluation for expressions involving user variables is undefined. For example, there is no guarantee that SELECT #a, #a:=#a+1 evaluates #a first and then performs the assignment.
What's more, this is a "feature" that is subject to removal:
Previous releases of MySQL made it possible to assign a value to a user variable in statements other than SET. This functionality is supported in MySQL 8.0 for backward compatibility but is subject to removal in a future release of MySQL.
So you should not rely on that, and rewrite your queries.
The MySQL documentation is quite clear on this point:
As a general rule, other than in SET statements, you should never assign a value to a user variable and read the value within the same statement.
That your code ever worked was something of a coincidence. The reason for such warnings is that behavior may change between releases; or even that the results may be inconsistent between runs on the same database.
You should replace the variables with window functions. The exact function is a bit unclear. Sample data, desired results, and a clear explanation of the results always helps.

After update to MySQL 8 a reliable stored procedure gives errors after applying change

A stored procedure developed some years ago, which employs many views to update a newly created table, is giving errors since upgrading to MYSQL 8. I can run the final View contained in the procedure, which calls all the others and it runs fine. But if I run the procedure it gives a divide by zero error on the first update query. I can't find anything wrong elsewhere. I can repeat the problem by taking a routine that works, adding a blank like so that there's a change and clicking 'accept'. Then the same routine gives the errors. The errors are:Error Code: 1264. Out of range value for column '....' at row 1, which is a decimal (5,2) and if I increase to decimal (6,2) I get Error Code: 1365. Division by 0.
Help!
This is likely due to different SQL modes between server setups. MySQL has SQL modes ERROR_FOR_DIVISION_BY_ZERO and strict mode, which determine if an error is raised when by zero happens or if the result will be determined to be NULL.
Your original procedures were written (procedures keep their modes) when the settings were different from your current one. When you change the code, the procedure get the current mode.

MySQL allow to select column that is neither aggregate nor specified in group by. Can this behavior be disabled?

It seems to me like MySQL is setting up a subtle trap for me. It allows to select column that is neither aggregate function like sum nor specified in group by clause. Therefor value in that column from my point of view is totally random.
I think in Oracle database that wasn't possible (but I used it like 5 years ago so something might change in this regard).
Is there any way to prevent from such nasty/trap-a-like behavior or at least tell it to warn me? Tools should work with developer not set up traps.
I'm using MySQL 5.6.30 and MySQL Workbench 6.3.7
The behaviour you describe is known. MySQL gives the correct result set for any valid and sane query. It can return unexpected data on queries that make no sense.

MySQL Configuration to auto truncate decimal value

I've a table with a field DECIMAL(9,2) and I've 2 servers running both mysql 5.7
If I run this code
INSERT INTO `money_accounts` (`balance`) VALUES (9999999999.99);
On one of the servers it got inserted and value is truncated, on the other it raise a Out of range value for column balance error.
My question is, what is the configuration value that makes this happen? or why it's happening in one server and not in the other?
The definition
DECIMAL(9,2)
means 9 digits of total precision, with 2 digits after the decimal place. The value
9999999999.99
has 12 total digits of precision, with 2 after the decimal place. Hence, the value is out of range. Rightfully, MySQL should have thrown an out of range error in both cases. In the case where it "worked," my guess is that truncation occurred.
By the way, you should be using DECIMAL(12,2) or wider to store the value in your question.
Update:
One possible explanation for why one of your servers was doing the insertion while the other failed is that the first has traditional mode turned off. Run the following on both servers:
SELECT ##SESSION.sql_mode
If you see output looking like
STRICT_TRANS_TABLES, STRICT_ALL_TABLES, ...
then the server is running in traditional mode, which means it won't truncate but will reject. To turn it off, try running
SET SESSION sql_mode=''
and them the insert should succeed (with truncation). But in any case, you should not be relying on truncation in production. If you need more precision, then widen the column.
Reference: Automatically trimming length of string submitted to MySQL
If we read the documentation: http://dev.mysql.com/doc/refman/5.7/en/out-of-range-and-overflow.html
When MySQL stores a value in a numeric column that is outside the
permissible range of the column data type, the result depends on the
SQL mode in effect at the time
If strict SQL mode is enabled, MySQL rejects the out-of-range value
with an error, and the insert fails, in accordance with the SQL
standard.
If no restrictive modes are enabled, MySQL clips the value to the
appropriate endpoint of the range and stores the resulting value
instead.
So it means that You should set necessary sql mode that will not fail when out-of-range error happens.
There is no config parameter about that.
And resolution of same issue we can see here that says that disabling STRICT_TRANS_TABLES and STRICT_ALL_TABLES modes can fix Your problem.

MySQL - Cloning then changing a field - Is this a race condition?

While debugging an issue in our software system, I came across a prepared statement that looks something like this:
"UPDATE Command SET expiredWhen=status, status='expired' WHERE id=?;"
The expiredWhen field is supposed to be set to whatever value is in the status field, then the status field is supposed to be set to "expired". It worked as expected in our MySQL 5.1 environment, but in MySQL 5.5 expiredWhen is being set to "expired".
What is the expected result of this statement? Is this a race condition, and we were lucky that it ever worked? Or is there defined behavior for this statement that changed from version 5.1 to 5.5?
The documentation is consistent between versions 5.1 and 5.5:
Single-table UPDATE assignments are generally evaluated from left to
right. For multiple-table updates, there is no guarantee that
assignments are carried out in any particular order
This suggests that it shouldn't be a race condition. That said, the word "generally" is a little concerning - it would probably be safest to assume that it could be a race condition.