#mysql #sql
#mysql #sql
Вопрос:
Я выполняю выбор в 5.7.32, где я хочу отрицать значения:
SELECT (val * -1) AS new_value
Это приводит к ошибке:
Data truncation: BIGINT UNSIGNED value is out of range i
Как это можно преодолеть?
Комментарии:
1. Ну, зачем тип данных
BIGINT UNSIGNED
, если вам нужно, чтобы значение было отрицательным?
Ответ №1:
Вы можете cast
signed
сначала:
select cast(val as signed) * -1 as new_value from mytable
Обратите внимание, что вы также можете сформулировать это как:
select - cast(val as signed) as new_value from mytable
Как ни странно, оба следующих выражения работают нормально, без явного приведения:
select - val as new_value from mytable;
select val / -1 as new_value from mytable;
Обратите внимание, что неподписанные наборы данных имеют максимально допустимое значение, чем эквивалентные беззнаковые (в основном это то же количество возможных значений, но также включая отрицательные значения). Таким образом, в основном, как прокомментировал Билл Карвин, значения, превышающие максимальное 32-разрядное целое число со знаком, не будут правильно обработаны.
Комментарии:
1. @ysth: мы можем получить эту ошибку с любым
unsigned
значением (кроме0
). Смотрите: db-fiddle.com/f/v42SJCyPZ6Vux5HWjKnZBY/02. ах, вы правы. Я испортил свое тестирование.
3. кажется
-val
, сбой происходит только при слишком больших значениях, ноval * -1
всегда сбой? Я этого не понимаю.4. Обратите внимание, что это приведет к усечению значений BIGINT, превышающих максимальное 32-разрядное целое число со знаком.
5. @BillKarwin нет, он не усекает, по крайней мере, в 5.7, он просто обрабатывает >= 2 ** 63 как отрицательное, а не положительное.
Ответ №2:
Вы можете пропустить строки, где оно находится вне диапазона:
select -val as new_value from foo where val <= 9223372036854775807
или вы можете просто обработать его как строку:
select concat('-',val) as new_value from foo;
приведение к signed не будет работать, поскольку оно изменит значения, превышающие 9223372036854775807, на отрицательные числа, которые при отрицании будут положительными.
Вы можете преобразовать его обратно в число с помощью:
select 0 concat('-',val) as new_value from foo;
но это приведет к потере точности для больших чисел.