MySQL 'commands out of sync' error when aggregate subtraction - mysql

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

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 Subquery Truncating Result with NULLIF

I have the following query:
SELECT
NULLIF(MAX(t.date),'2019-01-15') AS ended
FROM
totals t
This query correctly outputs a date:
> 2019-01-01
But if I reference this query inside of a subquery like so:
SELECT * FROM
(SELECT
NULLIF(MAX(t.date),'2019-01-15') AS ended
FROM
totals t) AS a
This version incorrectly produces a truncated result:
> 201
Can someone help me to understand this behavior and how best to work around it?
Additional Notes:
I am running MySQL version: "5.7.25 MySQL Community Server"
For anyone wanting to test this out, here is an example of a simple test table that is affected by this problem:
CREATE TABLE `totals` (
`date` date NOT NULL,
`value` decimal(10,0) DEFAULT NULL,
PRIMARY KEY (`date`)
);
INSERT INTO `totals` VALUES ('2018-01-01',2000000),('2019-01-01',3000000);
The issue here appears to be a subtle converting/casting issue happening within NULLIF. First, here is a version of your query which does in fact work as expected:
SELECT *
FROM
(
SELECT NULLIF(MAX(t.date), STR_TO_DATE('2019-01-15', '%Y-%m-%d') AS ended
FROM totals t
) AS a
What is happening with your current query is that MySQL is converting the call to MAX(t.date) to text, to match the text literal '2015-01-15'. By ensuring that both arguments to NULLIF are date type, you get the behavior you want.
As for why we are seeing 201 as the string result from the call to NULLIF, I don't have an explanation. But, I can cite the documentation for NULLIF here:
Returns NULL if expr1 = expr2 is true, otherwise returns expr1. This is the same as CASE WHEN expr1 = expr2 THEN NULL ELSE expr1 END.
It is a general rule in SQL that both the if and else branches of a CASE expression should always have the same type. Actually, if we violate this rule, in most databases the CASE expression won't even compile. Applying this to NULLIF means that we should always make sure that both arguments have the same type. Breaking from this rule might run on MySQL (similar to doing a non ANSI compliant GROUP BY with full mode turned off), but it is not something we should choose if we can avoid it.
My guess is that there's implicit datatype conversions happening.
As a workaround, I would take the return from the NULLIF function and convert/cast it back to DATE datatype. Simplest would be to wrap it in DATE() function.
SELECT
DATE( NULLIF(MAX(t.date),'2019-01-15') ) AS ended
^^^^^ ^
We could also try converting the string literal, convert that to a DATE, and see if that fixes the problem:
SELECT
NULLIF(MAX(t.date), DATE('2019-01-15') ) AS ended
^^^^^ ^
Or we can do both:
SELECT
DATE( NULLIF(MAX(t.date), DATE('2019-01-15') ) ) AS ended
^^^^^ ^
^^^^^ ^
There are other expressions we can use for the datatype conversion such as CAST(), CONVERT(), or STR_TO_DATE().
Or we could just use the simple + INTERVAL 0 DAY trick. e.g.
SELECT
NULLIF(MAX(t.date),'2019-01-15' + INTERVAL 0 DAY ) + INTERVAL 0 DAY AS ended
^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^

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 how to make negative results possible when subtracting unsigned values?

I have three tables joined by left join. Here's the code:
SELECT
(LEAST(`a`.`price, `b`.`price`) - `c`.`price`) AS `diff`
...
ORDER BY `diff` DESC
The problem: c.price is greater than the LEAST, thus the subtraction is negative and throws BIGINT UNSIGNED value is out of range.
How can I make it NOT throw this ridiculous error?
This is result data, I'm not modifying the actual data in the table, so why does it not allow me to do this normally?
I've tried CAST(LEAST(...) AS SIGNED) and casting both columns inside LEAST as signed, neither worked.
Cast as SIGNED each number before LEAST and before substract
SELECT
(LEAST(CAST(`a`.`price` AS SIGNED), CAST(`b`.`price` AS SIGNED)) - CAST(`c`.`price` AS SIGNED)) AS `diff`
...
ORDER BY `diff` DESC
You may want to check the NO_UNSIGNED_SUBTRACTION operator: http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html#sqlmode_no_unsigned_subtraction.
There are risks in using it, though: http://datacharmer.blogspot.fi/2006/11/hidden-risks-of-sql-mode.html

BIGINT Out-of-range Error since MySQL 5.5

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);