MySQL BIGINT UNSIGNED value is out of range - mysql

I have multiple triggers on my database which is updating a statistics table. This table has a BIGINT UNSIGNED column, but on updating the table, I sometimes catch the error: BIGINT UNSIGNED value out of range. I guess the value that is being inserted results in a smaller value then 0, but that seems very strange behavior in my opinion, given the IF check in my statements.
The statement I use:
UPDATE TABLE SET FileSize=774982 WHERE ID=10;
The trigger causing the problem is defined as:
IF (OLD.FileSize <> NEW.FileSize) THEN
INSERT INTO Statistics VALUES('FileSize', 0, 0, 0)
ON DUPLICATE KEY UPDATE Value = IF((Value - OLD.FileSize) < 0, 0, Value - OLD.FileSize);
INSERT INTO Statistics VALUES('FileSize', 0, 0, NEW.FileSize)
ON DUPLICATE KEY UPDATE Value = Value + NEW.FileSize;
END IF
As you can see, I only update the statistics table if the FileSize has really changed (OLD.FileSize <> NEW.FileSize). Besides, I have also added a check to make sure I don't get values below 0 with IF((Value - OLD.FileSize) < 0, 0, Value - OLD.FileSize). Though, the value get's an out of range error and it seems very unlikely that it goes out the upper bound of the BIGINT UNSIGNED, given the fact that the following update statements work? The Error is not always triggered it seems...?
The trigger consists of two statements as you can see. The simple explanation is that the first statement removes the old file size form the statistics table and the second statement is adding the new file size to the table. (This calculates the difference and updates the statistics table correctly)
So, I don't understand how I can get the error: Database error: BIGINT UNSIGNED value is out of range in '(DB.Statistics.Value - OLD.FileSize)' (error code: 1690, State: 22003)
The new and old file sizes are 244662 and 774982 respectively (in bytes)

When you subtract an UNSIGNED value, the result is also UNSIGNED, which means it can't be negative. You're getting the error when you try to calculate Value - OLD.FileSize in the IF() condition.
Instead of subtracting, use a comparison.
INSERT INTO Statistics VALUES('FileSize', 0, 0, 0)
ON DUPLICATE KEY UPDATE Value = IF(Value < OLD.FileSize, 0, Value - OLD.FileSize);

Related

Should I input 2 digit numbers in TINYINT?

I need a column that can store values from -1 (negative 1) to +15 (positive 15).
I am using a TINYINT(2) column at the moment and it works just fine.
I need to know if this is ideal and safe to use because I read online that TINYINT should ONLY be used for binary value such as 1 and 0.
MySQL TINYINT has a storage of 1 byte.
Unsigned value in range 0 to 255 or signed value of range -128 to 127 can be stored.
Also note that 2 in TINYINT(2) does not limit the length of your value to 2 digits. more info in this link - What is the size of column of int(11) in mysql in bytes?
TINYINT will store your value perfectly fine; however it will not guarantee that it will be correct (exactly in that range). Please note that (2) is just related to padding when selecting via the command line and it has nothing to do with the limit. Normally you do not need it at all. TINYINT (signed) will store values from -128 to 127 regardless if you add (n) at the end of it or not.
If you want to make sure that the value is always exactly in the range between -1 and 15, you have two options:
Using CHECK constraint over the existing TINYINT (recommended solution):
CREATE TABLE tiexample(
val TINYINT NOT NULL,
CONSTRAINT val_range CHECK(val>=-1 AND val<=15)
);
INSERT INTO tiexample(val) VALUES(122);
ERROR 4025 (23000): CONSTRAINT `val_range` failed for `test`.`tiexample`
INSERT INTO tiexample(val) VALUES(12);
Query OK, 1 row affected (0.001 sec)
Listing all values as ENUM:
CREATE TABLE tiexample(
val ENUM("-1","0","1","2","3","4","5","6","7","8","9","10","11","12","13","14","15") NOT NULL,
CONSTRAINT val_range CHECK(val<>"")
);
INSERT INTO tiexample(val) VALUES("122");
ERROR 4025 (23000): CONSTRAINT `val_range` failed for `test`.`tiexample`
INSERT INTO tiexample(val) VALUES("12");
Query OK, 1 row affected (0.001 sec)
Note that using this method you must insert the values with quotes (like "8", not just 8). Also first it may seem strange that we use CHECK constraint here but it is needed because by default inserting invalid value on ENUM field will insert an empty string in the table (not NULL - it's an empty string). You can get around that issue and skip the CHECK constraint if you use this sql mode before inserting into the table:
SET SQL_MODE = 'Traditional';
Personally I would not struggle with the ENUM type. It is more suitable for different use-cases.
Footnote: CHECK constraints are available and working only in MySQL MySQL 8.0.16+ and MariaDB 10.2.1+. Prior versions accept the constraint but never use it (it will ignore them).

Unexpected result using SELECT ... WHERE id = 0 on VARCHAR id in MySQL

I'm using MySQL 8 with InnoDB with a node server with mysql2 driver.
My table looks like:
CREATE TABLE IF NOT EXISTS users(
id VARCHAR(36) NOT NULL,
name VARCHAR(32) NOT NULL,
email VARCHAR(255) NOT NULL,
...
PRIMARY KEY (id)
)
I use no auto increment and as VARCHAR ids, I use time based UUIDs.
If I now do my SELECT query:
SELECT * FROM users where id = 'some valid id';
I get my expected result.
If I do:
SELECT * FROM users where id = '0';
I get nothing, because no id in my table has the value '0'.
BUT, if i do:
SELECT * FROM users where id = 0;
I get the last inserted row, which has, of course, a valid VARCHAR id different from 0.
This behavior occured on my node server by accident, because JS sometimes interpretes undefined as 0 in http querys.
In consequence I can easyly avoid inserting 0 in my querys (what I do now), but I would like to understand why this happens.
Your id is varchar(), so this comparison:
WHERE id = 0
requires type conversion.
According to the conversion rules in SQL, the id is turned into a string. Now, in many databases, you would get an error if any values of id could not be converted into numbers.
However, MySQL supports implicit conversion with no errors. (You can read about such conversion in the documentation.) This converts all leading digits to a number -- ignoring the rest. If there are no leading digits, then the value is zero. So, all these are true in MySQL:
'a' = 0
'0a' = 0'
'anything but 0!' = 0
There are two morals to this story.
If you really want id to be a number, then use a number data type (int, bigint, decimal).
Don't mix types in comparisons.

MySQL Variable Returning Incorrect Value

The Issue
I have a stored proc in a DB server that's bringing back a value of 5064803 when that record does not exist and the value should be 5064800 as per the query that builds the value of the variable.
I'm not sure if this is an issue with the value being of the FLOAT data type and the value in the record of the table ending in a double-zero or what but I cannot figure it out easily.
The table data types match those from the sensors that are set but this particular value from this sensor never actually gets set to a data type and it's usually always either a 1-8 digit INT with no decimal but I'd like to keep the data types the same as the correlated sensor just in case.
I've broke down the proc and I'm able to recreate the problem easily so I will post the detail below for those that may be able to help me figure out the issue and any workaround, etc.
The SQL Data
Create Table
delimiter $$
CREATE TABLE `number` (
`TimeInt` varchar(10) NOT NULL,
`TimeStr` datetime NOT NULL,
`IsInitValue` int(11) NOT NULL,
`Value` float NOT NULL,
`IQuality` int(11) NOT NULL,
UNIQUE KEY `uk_Times` (`TimeInt`,`TimeStr`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8$$
Insert Data
INSERT INTO `Number` (`TimeInt`,`TimeStr`,`IsInitValue`,`Value`,`IQuality`) VALUES ('1502618950','2017-08-13 10:09:10',1,5064800,0);
INSERT INTO `Number` (`TimeInt`,`TimeStr`,`IsInitValue`,`Value`,`IQuality`) VALUES ('1502618796','2017-08-13 10:06:36',0,5064800,3);
INSERT INTO `Number` (`TimeInt`,`TimeStr`,`IsInitValue`,`Value`,`IQuality`) VALUES ('1502617167','2017-08-13 09:39:27',1,5063310,0);
INSERT INTO `Number` (`TimeInt`,`TimeStr`,`IsInitValue`,`Value`,`IQuality`) VALUES ('1502613355','2017-08-13 08:35:55',0,5063310,3);
INSERT INTO `Number` (`TimeInt`,`TimeStr`,`IsInitValue`,`Value`,`IQuality`) VALUES ('1502612814','2017-08-13 08:26:54',1,0,0);
INSERT INTO `Number` (`TimeInt`,`TimeStr`,`IsInitValue`,`Value`,`IQuality`) VALUES ('1502609015','2017-08-13 07:23:35',0,0,3);
The SQL Query Breakdown
SET #bStartTime = '2017-08-13 09:24:16';
SET #bEndTime = '2017-08-13 10:06:31';
SET #LastNumber = (SELECT Value FROM Number ORDER BY TimeStr DESC LIMIT 1);
SET #NowNumber = (SELECT Value FROM Number WHERE TimeStr BETWEEN #bStartTime AND #bEndTime ORDER BY TimeStr DESC LIMIT 1);
SELECT #NowNumber;
SELECT #LastNumber;
Recreating the Issue
So based on The SQL Query Breakdown above, once all the data is in the table and then I run the queries within the SELECT queries alone within the #NowNumber and/or #LastNumber variables, I get the correct result of 5064800. However, if I run the entire SET statements for both of those to have it set the query and then just do a SELECT of those variable, it brings back the wrong result of 5064803.
So for example if I run SELECT Value FROM Number ORDER BY TimeStr DESC LIMIT 1 then the correct value is returned. If I run SET #LastNumber = (SELECT Value FROM Number ORDER BY TimeStr DESC LIMIT 1); and then run SELECT #LastNumber; I get the incorrect value returned.
Server System Specs
This particular MySQL Server is running the x86 version of 5.5.50 on Windows Server 2008 with 144 GB of RAM for some quick specs.
Question
I'd like to know what is causing this, and if there is a workaround to the problem either with or without changing the data type of the column assuming that's the issue when it's returned as a variable rather than just a straight query result.
I'll be happy to disclose more technical specs of the environment if needed but I've included what I think it important for the question. Perhaps this is a version bug or there's something obvious that causes this that I cannot see easily so I'm hoping someone can help me with this or explain why this is or is not possible with MySQL.
Sorry, declares can only be used in stored procedures in MySQL. I found this article which may help. It explains how MySQL rounds when storing digits and recommends using doubles. Try changing your floats to doubles.
MySql FLOAT datatype and problems with more then 7 digit scale

Cannot insert zero in to MySQL INT NULL

I have a query that save zero in one of the MySQL column.
Column was set to be NULL = no and Default = none. But my query wasnt working when i had that structurer when I save 0. So i change the value to null. But when saving 0 (zero) in database its still getting saved NULL.
When i had the column set to be NULL = no and Default it was working fine on my localhost but in my hosting query wasn't getting inserted. that is why i change it to null.
MySQL version 5.6.30
Query
$mysqli->query("INSERT INTO categories(cname, cat_description, cat_ parent) VALUES ('$cname', '$description', '0')");
You are actually trying to insert a string in an integer column. MySQL does not accept this. And it shouldn't.
Insert an integer:
$mysqli->query("INSERT INTO categories(cname, cat_description, cat_ parent) VALUES ('$cname', '$description', 0)");
Just remove the quotes around 0.

Query update issue

I m just confuse in this Query and i don't know how to solve this if you have any idea about this please help me or helps are definitely appreciated
I have table structure like this and test column contain 3 value
UPDATE `test` SET test = test -3
when i execute this Query the result will be show like this
UPDATE `test` SET test = test -4
But when i execute this query the result will not proper save in test column like this
0 result required or i don't need any subtract value also
Apparently you are using the BIGINT UNSIGNED data type. If you want to store negative numbers, you need to change it to a regular signed BIGINT (Also be sure to set it to NULL or NOT NULL as required):
ALTER TABLE test
MODIFY COLUMN test BIGINT;
UPDATE: If you actually want to store 0 instead of -4 in your example, you can do so using the GREATEST() function like this:
UPDATE `test` SET test = GREATEST(CAST(test AS SIGNED) - 4,0)
The problem is most likely due to the fact that your bigint is unsigned.
Per the documentation, an unsigned bigint is
A large integer. The signed range is -9223372036854775808 to
9223372036854775807. The unsigned range is 0 to 18446744073709551615.
Notice the unsigned range and how it is your number - 3 (4 comes from getting from 0 to xxx15 I believe)
So, you should only need to update your column to be a bigint that is not unsigned (signed) and this should work.
ALTER TABLE test MODIFY COLUMN test BIGINT SIGNED;
UPDATE
If you want to keep the BIGINT UNSIGNED so that you cannot have negative numbers, then you could write a trigger to force a 0, or you could just make your query something like this:
UPDATE test
SET test = CASE WHEN test >= value THEN test-value ELSE 0 END
Basically, if the value attempting to be subtracts is more than the current value, then just set the value to 0, otherwise perform the subtraction.
As per this question: MySQL: bigint Vs int
bigint's maximum value is 18,446,744,073,709,551,615
You're wrapping around to the highest value when you subtract from 0, since your bigint is unsigned.