BIGINT Out-of-range Error since MySQL 5.5 - mysql

I'm working with nested sets for my CMS but since MySQL 5.5 I can't move a node.
The following error gets thrown:
Error while reordering docs:Error in MySQL-DB: Invalid SQL:
SELECT baum2.id AS id,
COUNT(*) AS level
FROM elisabeth_tree AS baum1,
elisabeth_tree AS baum2
WHERE baum2.lft BETWEEN baum1.lft AND baum1.rgt
GROUP BY baum2.lft
ORDER BY ABS(baum2.id - 6);
error: BIGINT UNSIGNED value is out of range in '(lektoren.baum2.id - 6)'
error number: 1690
Has anyone solved this Problem? I already tried to cast some parts but it wasn't successful.

BIGINT UNSIGNED is unsigned and cannot be negative.
Your expression ABS(lektoren.baum2.id - 6) will use a negative intermediate value if id is less than 6.
Presumably earlier versions implicitly converted to SIGNED. You need to do a cast.
Try
ORDER BY ABS(CAST(lectoren.baum2.id AS SIGNED) - 6)

SET sql_mode = 'NO_UNSIGNED_SUBTRACTION';
Call this before the query is executed.

ORDER BY ABS(CAST(lectoren.baum2.id AS BIGINT SIGNED) - 6)
That change would be mysql only.
instead, do
ORDER BY ABS(- 6 + baum2.id);

Related

How to avoid type lock in SELECT query

I have a table services with column 'some_col' bigint(20) unsigned NOT NULL.
When I tried to select rows from services with value converting to negative numbers like this:
SELECT -1 * some_col AS neg_some_col, some_other_columns...
FROM services
WHERE condition
I facing with following error:
Error Code: 1690. BIGINT UNSIGNED value is out of range in '(-(1) * `services`.`some_col`)'
How I can avoid this restriction and select column with convertion?
I have no options to proccess selected rows further (behind MySQL). Also would be nice to do it with single request (subqueries possible).
CAST(column AS SIGNED)
Does the trick! See docs
You can also edit MySQL config to allow this, by setting:
sql_mode='NO_UNSIGNED_SUBTRACTION'
See here. But this would allow it for the entire server of course.

MySQL - Comparing two dates in the WHERE clause

I have a table, links that links two categories (parent & child) together. The table has five fields:
autoinc
parent_category_name year(4) NOT NULL
parent_category_year varchar(255) NOT NULL
child_category_name year(4) NOT NULL
child_category_year varchar(255) NOT NULL
I am trying to write an INSERT SELECT that grabs both the parent & child category_id, and inserts it into a temp table.
INSERT INTO temp (parent_category_id, child_category_id)
SELECT parent.parent_category_id, child.child_category_id
FROM links
JOIN categories AS parent
ON parent.name = link.parent_category_name
AND parent.year = link.parent_category_year
JOIN categories AS child
ON child.name = link.child_category_name
AND child.year = link.child_category_year
This query works fine, but I need to apply some business rules. The rules are:
The parent year must be the same as the child year
OR
The parent year must be one year less than the child year
I've added a WHERE clause to my query:
WHERE link.child_category_year = link.parent_category_year
OR link.child_category_year - link.parent_category_year = 1
When this INSERT statement executes in my Perl code, I get the following exception:
DBI Exception: DBD::mysql::db do failed: BIGINT UNSIGNED value is out of range in '(`my_database`.`links`.`child_category_year` - `my_database`.`links`.`parent_category_year`)' [for Statement "
So, I take it that the INSERT does not like my date subtraction in the WHERE clause. I explored using the DATEDIFF function, but I am not simply looking for a one year difference in dates, but rather one year less on the parent than the child.
How can I accomplish this without the insert error?
OR link.child_category_year - link.parent_category_year = 1 may produce negative substraction result. Of course negative value cannot be stored as UNSIGNED.
Simply convert your expression to
OR link.child_category_year = link.parent_category_year + 1
Totally unclear why "year" would be stored as an unsigned bigint. That is way overkill for my understanding of "year".
In any case, why not rephrase the logic to:
WHERE link.parent_category_year = link.child_category_year
link.parent_category_year = link.child_category_year + 1
In general, you should not have a problem adding 1 to an unsigned value.
This is my first time knowing that there's a YEAR datatype and after reading the official documentation I understand that:
MySQL displays YEAR values in YYYY format, with a range of 1901 to 2155, and 0000.
Although, it doesn't specifically said that the type is UNSIGNED but the last line in the documentation says:
If strict SQL mode is not enabled, MySQL converts invalid YEAR values to 0000. In strict SQL mode, attempting to insert an invalid YEAR value produces an error.
That means you received the error because one (or more) of the subtracted value in the condition of OR link.child_category_year - link.parent_category_year = 1 is returning negative.
Now, there are a few options you could try:
You can use CAST function and change the year data as SIGNED like :
WHERE link.child_category_year = link.parent_category_year
OR CAST(link.child_category_year AS SIGNED) - CAST(link.parent_category_year AS SIGNED) = 1
Or you can set NO_UNSIGNED_SUBTRACTION sql_mode and run the query as is:
SET sql_mode='NO_UNSIGNED_SUBTRACTION'
You can also consider changing the YEAR datatype to INTEGER and run the query as is:
ALTER TABLE links
MODIFY parent_category_year INT NOT NULL,
MODIFY child_category_year INT NOT NULL;

MySQL automatic string to integer casting in where clause?

I am not new to MySQL but a strange situation happened in my code today coincidently which got me surprised. Can someone explain why this gives me identical results?
SELECT * FROM `products` WHERE id = 12
and
SELECT * FROM `products` WHERE id = '12ABC'
In both cases I get the same result with the same record being selected. I would expect that second one would return me nothing?! My ID field is int(11) unsigned with auto_increment flag turned on.
From MySQL docs:
When an operator is used with operands of different types, type conversion occurs to make the operands compatible
Documentation
So basically, '12ABC' is cast to 12.
MySQL has to make a conversion to make a compare betwen 2 different types. It tries to make the string to an int and get the digits from the string starting from the beginning.
It you had for instance
'ABC12'
the result of the string conversion to int would be 0

SQL statement convert varchar to integer

I was having some problem when trying to convert varchar field in MySQL to integer using SQL statement:
SELECT mrtpopTime, CONVERT(INT,mrtpopAmt)
FROM tm_mrtpop
WHERE mrtpopName = ''
ORDER BY CONVERT(INT, mrtpopAmt) DESC
I am trying to get the top 3 records when mrtpopAmt was arranged in a way where it is in reverse order. However, I am getting error message at the INT and the error message is:
Syntax error, Unecpected INT_SYM
I wonder why is it so? Thanks in advance.
This is because MySQL doesn't use CONVERT() for casting. It uses the CAST function. In your case you would use:
CAST(mrtpopAmt AS SIGNED) -- This can also be UNSIGNED if it will always be a positive integer

MySQL 'commands out of sync' error when aggregate subtraction

I am trying to use an aggregate function in MySQL to return the balance of an account. However I keep getting an error an it's driving me nuts!
SELECT cast(sum((number_sold - number_bought) * price) as unsigned) as 'Balance'
FROM transactions
This returns:
#2014 - Commands out of sync; you can't run this command now
Here's an SQLfiddle: http://sqlfiddle.com/#!2/26297/1
Any help appreciated!
As mentioned by Xepoch here,
If either the left-hand side or the right-hand side of the subtraction
operator is unsigned, the result is unsigned as well. You can change
this by setting the NO_UNSIGNED_SUBTRACTION SQL mode.
Alternatively, you can also explicitly cast your unsigned values to be
signed bigint values and then do the subtraction.
Thus,
SELECT (cast(number_sold AS signed) - cast(number_bought AS signed)) * price AS 'Balance'
FROM transactions