Ein seltsames Betriebsproblem in SQL Server: -100 / -100 * 10 = 0

106
  • Wenn Sie SELECT -100/-100*10das Ergebnis ausführen, ist 0.
  • Wenn Sie SELECT (-100/-100)*10das Ergebnis ausführen, ist 10.
  • Wenn Sie SELECT -100/(-100*10)das Ergebnis ausführen, ist 0.
  • Wenn Sie SELECT 100/100*10das Ergebnis ausführen, ist 10.

BOL sagt:

Wenn zwei Operatoren in einem Ausdruck dieselbe Operatorprioritätsstufe haben, werden sie basierend auf ihrer Position im Ausdruck von links nach rechts ausgewertet.

Und

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

Ist BOL falsch oder fehlt mir etwas? Es scheint, dass das -den (erwarteten) Vorrang abwirft.

cuizizhe
quelle
7
Was ist deine Frage?
Ilyes
14
Warum denkst du, hast du mit Bits zu tun, du arbeitest mit ganzen Zahlen. Und Ganzzahl / Ganzzahl = Ganzzahl. Also -100 / -1000 ist 0
sepupic
5
OK, ich stimme zu, das -scheint den Fluss "falsch" zu machen. Wenn Sie versuchen, erhalten -100/(-100)*10Sie das Ergebnis 10. es scheint, dass das /gegen den Wert -in der Gleichung angewendet wird und dann die Gleichung 100*10bestimmt wird. Ich bin nicht sicher, ob dies ein Fehler bei BOL ist, aber mehr noch, dass sich SQL Server nicht wie erwartet verhält. Es könnte sich lohnen, ein Problem mit SQL-Dokumenten anzusprechen und zu sehen, wie sie darauf reagieren. Vielleicht könnte der Dokumentation, die über die "Funktion" informiert, ein Hinweis hinzugefügt werden.
Larnu
3
SELECT -100/(-100)*10gibt auch 10 zurück. Es sieht so aus, -als würde der -Operator behandelt, der erst angewendet werden sollte, nachdem er 100*10berechnet wurde
Panagiotis Kanavos
7
A / -B * Cist A <div> <negate> B <multiply> C. Negieren hat gemäß den Dokumenten eine niedrigere Priorität als Multiplizieren. Das Ergebnis ist also A / -(B * C). Sie können mit Floating - Konstanten dies deutlicher sehen: 12e / -13e * 14evs 12e / (-13e) * 14evs 12e / 13e * 14e.Die Grunde , dies uns abwirft, weil wir in der Regel einstelliger minus erwarten Teil der wörtlichen zu werden, oder zumindest eine sehr hohe Priorität, aber das ist nicht , wie T-SQL funktioniert.
Jeroen Mostert

Antworten:

96

Gemäß der Präzedenztabelle, dies ist das erwartete Verhalten. Der Operator mit höherer Priorität ( /und *) wird vor dem Operator mit niedrigerer Priorität (unär -) ausgewertet . Also das:

-100 / -100 * 10

wird bewertet als:

-(100 / -(100 * 10))

Beachten Sie, dass sich dieses Verhalten von den meisten Programmiersprachen unterscheidet, bei denen die unäre Negation eine höhere Priorität hat als die Multiplikation und Division, z . B. VB , JavaScript .

Salman A.
quelle
38
Wow, ein weiteres Juwel in T-SQL :) Ich denke, ich muss jetzt meinen gesamten Code prüfen, um nach Fehlern zu suchen.
usr
14
Oh Mann. Dies ist noch schlimmer als die verschiedenen Fehler des ternären PHP- Operators bugs.php.net/bug.php?id=61915 . Welche vernünftigen Leute denken, dass unäre Operatoren eine niedrigere Priorität haben müssen als binäre?
Phuclv
12
Der wirkliche Unterschied kann sein, ob -als Operator in betrachtet wird -100. In einigen Sprachen ist es Teil der Syntax einer Ganzzahl.
Barmar
7
Es ist also ein Fehler in ihrer Priorität von unär -.
Kevin
4
Und der Gewinner des kontraintuitiven Designs ist ...: Microsoft - noch einmal
rexkogitans
34

BOL ist richtig. -hat eine niedrigere Priorität als *, so

-A * B

wird analysiert als

-(A * B)

Da die Multiplikation so ist, wie sie ist, bemerken Sie dies normalerweise nicht, außer wenn Sie die beiden anderen binären Operatoren mit gleicher Priorität einmischen: /und %(und %wird in solchen zusammengesetzten Ausdrücken selten verwendet). So

C / -A * B

Wird analysiert als

C / -(A * B)

Erklärung der Ergebnisse. Dies ist nicht intuitiv, da in den meisten anderen Sprachen unäres Minus eine höhere Priorität als *und hat /, jedoch nicht in T-SQL, und dies wird korrekt dokumentiert.

Eine schöne (?) Art, es zu veranschaulichen:

SELECT -1073741824 * 2

erzeugt einen arithmetischen Überlauf, da -(1073741824 * 2)produziert 2147483648als Zwischenprodukt, das in einem nicht paßt INT, aber

SELECT (-1073741824) * 2

erzeugt das erwartete Ergebnis -2147483648, was tut.

Jeroen Mostert
quelle
"Minus" ist binär. Unary -ist "negativ". Leute, die Dinge wie "minus 10" sagen, wenn sie "negative 10" bedeuten, sind ungenau.
Akkumulation
11
@Acccumulation: Die Ungenauigkeit ist nicht meine. Der -Betreiber, wenn sie an einen einzelnen Operanden angewendet wird , wird aufgerufen , MINUSin SQL - Abfrage - Pläne. Sein binäres Gegenstück heißt SUB. Wenn Sie möchten, interpretieren Sie "unäres Minus" als Abkürzung für "den durch das Minuszeichen gekennzeichneten unären Operator" - eher eine syntaktische als eine semantische Bezeichnung.
Jeroen Mostert
3
"negative 10" ist amerikanische Standardverwendung (glaube ich), aber in Großbritannien nicht Standard.
Alchymist
12

Beachten Sie in der Dokumentation, dass (möglicherweise nicht intuitiv) die Rangfolge für - (Negative)dritter ist.

So erhalten Sie effektiv:

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

Wenn Sie sie in Variablen einfügen, wird dies nicht angezeigt, da nach der Multiplikation keine unäre Operation auftritt.

Hier sind also A und B gleich, während C, D, E das Ergebnis zeigen, das Sie sehen (wobei E die vollständige Klammer hat).

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
Jamie Pollard
quelle