Странная проблема с работой в SQL Server: -100 / -100 * 10 = 0

106
  • Если выполнить, SELECT -100/-100*10результат будет 0.
  • Если выполнить, SELECT (-100/-100)*10результат будет 10.
  • Если выполнить, SELECT -100/(-100*10)результат будет 0.
  • Если выполнить, SELECT 100/100*10результат будет 10.

BOL заявляет:

Если два оператора в выражении имеют одинаковый уровень приоритета, они оцениваются слева направо в зависимости от их положения в выражении.

И

Level   Operators
  1     ~ (Bitwise NOT)
  2     * (Multiplication), / (Division), % (Modulus)
  3     + (Positive), - (Negative), + (Addition), + (Concatenation), - (Subtraction), & (Bitwise AND), ^ (Bitwise Exclusive OR), | (Bitwise OR)

BOL неправильный, или я что-то упускаю? Кажется, -это отбрасывает (ожидаемый) приоритет.

Cuizizhe
источник
7
Каков твой вопрос?
Ilyes
14
Как вы думаете, почему вы имеете дело с битами, вы работаете с целыми числами. И целое число / целое число = целое число. Итак -100 / -1000 равно 0
sepupic
5
Хорошо, я согласен, это -, кажется, приводит к тому, что поток идет «неправильно». Если попробуешь -100/(-100)*10, получишь результат 10. кажется, /что применяется против значения -в уравнении, а затем уравнение 100*10определяется. Я не уверен, что это ошибка BOL, но более того, SQL Server ведет себя не так, как ожидалось. Возможно, стоит поднять вопрос о sql-docs и посмотреть, какой там ответ; возможно, можно было бы добавить в документацию примечание, сообщающее об этой «особенности».
Ларну
3
SELECT -100/(-100)*10также возвращает 10. Похоже, -он рассматривается как -оператор, который следует применять только после 100*10вычисления
Панайотис Канавос
7
A / -B * Cесть A <div> <negate> B <multiply> C. Согласно документации, отрицание имеет более низкий приоритет, чем умножение, поэтому результат такой A / -(B * C). Вы можете увидеть это более четко, используя плавающие константы: 12e / -13e * 14evs. 12e / (-13e) * 14evs. 12e / 13e * 14eПричина, по которой это нас сбивает, заключается в том, что мы обычно ожидаем, что унарный минус станет частью литерала или, по крайней мере, будет иметь очень высокий приоритет, но T-SQL не так. работает.
Йерун Мостерт

Ответы:

96

В соответствии с таблицей приоритета, это является ожидаемым поведением. Оператор с более высоким приоритетом ( /и *) оценивается перед оператором с более низким приоритетом (унарный -). Итак, это:

-100 / -100 * 10

оценивается как:

-(100 / -(100 * 10))

Обратите внимание, что это поведение отличается от большинства языков программирования, где унарное отрицание имеет более высокий приоритет, чем умножение и деление, например, VB , JavaScript .

Салман А
источник
38
Ух ты, еще одна жемчужина в T-SQL :) Думаю, теперь мне нужно проверить весь свой код, чтобы найти ошибки.
usr
14
о чувак. Это даже хуже, чем ошибки различных тернарных операторов PHP bugs.php.net/bug.php?id=61915 . Какие здравомыслящие люди думают, что унарные операторы должны иметь более низкий приоритет, чем бинарные?
phuclv
12
Реальная разница может заключаться в том -, считается ли оператор в -100. В некоторых языках это часть синтаксиса целого числа.
Barmar
7
Так что это ошибка в их приоритете унарных -.
Кевин
4
И победитель контр-интуитивного дизайна ...: Microsoft - еще раз
rexkogitans
34

BOL правильный. -имеет более низкий приоритет, чем *, поэтому

-A * B

разбирается как

-(A * B)

Умножение является тем, чем оно является, вы обычно этого не замечаете, за исключением смешивания двух других двоичных операторов с равным приоритетом: /и %%редко используется в составных выражениях, подобных этому). Так

C / -A * B

Анализируется как

C / -(A * B)

объясняя результаты. Это нелогично, потому что в большинстве других языков унарный минус имеет более высокий приоритет, чем *и /, но не в T-SQL, и это правильно задокументировано.

Хороший (?) Способ проиллюстрировать это:

SELECT -1073741824 * 2

производит арифметическое переполнение, потому что -(1073741824 * 2)производит 2147483648как промежуточное звено, которое не помещается в INT, но

SELECT (-1073741824) * 2

дает ожидаемый результат -2147483648, что и происходит.

Йерун Мостерт
источник
«минус» - двоичный. Унарный -- это «отрицательный». Люди, которые говорят что-то вроде «минус 10», имея в виду «отрицательные 10», ошибаются.
Накопление 04
11
@ Накопление: неточность не моя. -Оператор, при применении к одним операндом, вызывается MINUSв планах запросов SQL. Его двоичный аналог называется SUB. Если хотите, интерпретируйте «унарный минус» как сокращение для «унарного оператора, обозначенного знаком минус» - синтаксическое, а не семантическое обозначение.
Йерун Мостерт
3
«Отрицательные 10» - это стандартное употребление в Америке (я считаю), но это не стандарт в Великобритании.
Alchymist
12

Обратите внимание в документации, что (возможно, что противоречит интуиции) порядок приоритета для - (Negative)третьего.

Таким образом вы получите:

-(100/-(100*10)) = 0

Если вы поместите их в переменные, вы этого не увидите, так как после умножения не происходит унарных операций.

Итак, здесь A и B одинаковы, тогда как C, D, E показывают результат, который вы видите (с E, имеющим полный брекетинг)

DECLARE @i1 int, @i2 int, @i3 int;

SELECT @i1 = -100,
       @i2 = -100,
       @i3 = 10;

SELECT @i1/@i2*@i3      [A],
       -100/(-100)*10   [B],
       -100/-100*10     [C],
       -100/-(100*10)   [D],
       -(100/-(100*10)) [E];

A - 10
B - 10
C - 0
D - 0
E - 0
Джейми Поллард
источник