Warum löst "SELECT POWER (10.0, 38.0);" einen arithmetischen Überlauffehler aus?

15

Ich bin Aktualisierung meines IDENTITYÜberlauf Skript check zu Konto für DECIMALund NUMERIC IDENTITYSpalten .

Im Rahmen der Prüfung berechne ich für jede IDENTITYSpalte die Größe des Datentypbereichs ; Ich benutze das, um zu berechnen, wie viel Prozent dieses Bereichs erschöpft sind. Für DECIMALund NUMERIC die Größe dieses Bereichs liegt2 * 10^p - 2 , wo pdie Genauigkeit.

Ich habe eine Reihe von Testtabellen mit DECIMALund NUMERIC IDENTITYSpalten erstellt und versucht, deren Bereiche wie folgt zu berechnen:

SELECT POWER(10.0, precision)
FROM sys.columns
WHERE 
       is_identity = 1
   AND type_is_decimal_or_numeric
;

Dies warf den folgenden Fehler:

Msg 8115, Level 16, State 6, Line 1
Arithmetic overflow error converting float to data type numeric. 

Ich habe es auf die IDENTITYSpalten des Typs eingegrenzt DECIMAL(38, 0)(dh mit maximaler Genauigkeit), also habe ich die POWER()Berechnung direkt auf diesem Wert versucht .

Alle folgenden Abfragen

SELECT POWER(10.0, 38.0);
SELECT CONVERT(FLOAT, (POWER(10.0, 38.0)));
SELECT CAST(POWER(10.0, 38.0) AS FLOAT);

führte auch zu dem gleichen Fehler.

  • Warum versucht SQL Server, die Ausgabe von POWER(), die vom Typ ist FLOAT, zu konvertieren NUMERIC(insbesondere, wenn FLOATsie eine höhere Priorität hat )?
  • Wie kann ich den Bereich einer DECIMALoder einer NUMERICSpalte für alle möglichen Präzisionen dynamisch berechnen (einschließlich p = 38natürlich)?
Nick Chammas
quelle

Antworten:

18

Aus der POWERDokumentation :

Syntax

POWER ( float_expression , y )

Argumente

float_expression
Ist ein Ausdruck vom Typ float oder von einem Typ, der implizit in float konvertiert werden kann .

y
Gibt die Potenz an, mit der float_expression ausgelöst werden soll . y kann ein Ausdruck der genauen numerischen oder ungefähren numerischen Datentypkategorie sein, mit Ausnahme des Bitdatentyps .

Rückgabetypen

Gibt den gleichen Typ zurück wie in float_expression übergeben . Wenn beispielsweise eine Dezimalzahl (2,0) als float_expression übergeben wird, ist das zurückgegebene Ergebnis eine Dezimalzahl (2,0).


Die erste Eingabe wird floatbei Bedarf implizit in umgewandelt .

Die interne Berechnung erfolgt floatrechnerisch mit der Standard-CRT-Funktion (C Runtime Library) pow.

Die floatAusgabe von powwird dann auf den Typ des linken Operanden zurückgewandelt (impliziert, numeric(3,1)wenn Sie den Literalwert 10.0 verwenden).

Die Verwendung eines Explizits floatfunktioniert in Ihrem Fall einwandfrei:

SELECT POWER(1e1, 38);
SELECT POWER(CAST(10 as float), 38.0);

Ein genaues Ergebnis für 10 38 kann nicht in einem SQL Server gespeichert werden, decimal/numericda dafür 39 Stellen Genauigkeit erforderlich wären (1 gefolgt von 38 Nullen). Die maximale Genauigkeit beträgt 38.

Martin Smith
quelle
23

Anstatt mich weiter in Martins Antwort einzumischen, füge ich den Rest meiner Erkenntnisse POWER()hier hinzu.

Halte deinen Schlüpfer fest.

Präambel

Zunächst stellePOWER() ich Ihnen A vor, die MSDN-Dokumentation für :

Syntax

POWER ( float_expression , y )

Argumente

float_expression Ist ein Ausdruck vom Typ float oder von einem Typ, der implizit in float konvertiert werden kann.

Rückgabetypen

Gleich wie float_expression.

Sie können aus dem Lesen der letzten Zeile schließen, POWER()die den Rückgabetyp hat FLOAT, aber noch einmal lesen. float_expressionist "vom Typ float oder von einem Typ, der implizit in float konvertiert werden kann". Trotz seines Namens float_expressionkann es sich also tatsächlich um eine FLOAT, eine DECIMALoder eine handeln INT. Da die Ausgabe von POWER()die gleiche wie die von ist float_expression, kann es sich auch um einen dieser Typen handeln.

Wir haben also eine Skalarfunktion mit Rückgabetypen, die von der Eingabe abhängen. Könnte es sein?

Beobachtungen

Ich präsentiere Ihnen Exponat B, einen Test, der zeigt, dass POWER()seine Ausgabe in Abhängigkeit von seiner Eingabe in verschiedene Datentypen umgewandelt wird .

SELECT 
    POWER(10, 3)             AS int
  , POWER(1000000000000, 3)  AS numeric0     -- one trillion
  , POWER(10.0, 3)           AS numeric1
  , POWER(10.12305, 3)       AS numeric5
  , POWER(1e1, 3)            AS float
INTO power_test;

EXECUTE sp_help power_test;

DROP TABLE power_test;

Die relevanten Ergebnisse sind:

Column_name    Type      Length    Prec     Scale
-------------------------------------------------
int            int       4         10       0
numeric0       numeric   17        38       0
numeric1       numeric   17        38       1
numeric5       numeric   17        38       5
float          float     8         53       NULL

Was passiert zu sein scheint , ist , dass die POWER()Abgüsse float_expressionin die kleinste Art , dass es passt, ohne BIGINT.

Daher SELECT POWER(10.0, 38);schlägt mit einem Überlauf - Fehler , da 10.0cast wird NUMERIC(38, 1)das nicht groß genug ist , um das Ergebnis von 10 zu halten , 38 . Dies liegt daran, dass 10 38 erweitert wird, um 39 Stellen vor der Dezimalstelle zu erhalten, wohingegen NUMERIC(38, 1)37 Stellen vor der Dezimalstelle und eine Stelle danach gespeichert werden können. Daher kann der Maximalwert NUMERIC(38, 1)10 37 - 0,1 betragen.

Mit diesem Verständnis kann ich einen weiteren Überlauffehler wie folgt beheben.

SELECT POWER(1000000000, 3);    -- one billion

Eine Milliarde (im Gegensatz zu einer Billion aus dem ersten Beispiel, auf das gegossen wird NUMERIC(38, 0)) ist gerade klein genug, um in eine zu passen INT. Eine auf die dritte Potenz erhöhte Milliarde ist jedoch zu groß INT, daher der Überlauffehler.

Einige andere Funktionen weisen ein ähnliches Verhalten auf, wobei der Ausgabetyp von der Eingabe abhängt:

Fazit

In diesem speziellen Fall ist die Lösung zu verwenden SELECT POWER(1e1, precision).... Dies funktioniert für alle möglichen Präzisionen, da es auf 1e1geworfen wird FLOAT, was lächerlich große Zahlen enthalten kann .

Da diese Funktionen so alltäglich sind, ist es wichtig zu verstehen, dass Ihre Ergebnisse aufgrund ihres Verhaltens gerundet sein oder Überlauffehler verursachen können. Wenn Sie einen bestimmten Datentyp für Ihre Ausgabe erwarten oder sich darauf verlassen, setzen Sie die entsprechende Eingabe nach Bedarf explizit um.

Also, Kinder, jetzt, wo Sie das wissen, können Sie ausgehen und gedeihen.

Nick Chammas
quelle