Frage

Ich aktualisiere mein IDENTITY Überlaufskript zu berücksichtigen DECIMAL und NUMERIC IDENTITY Säulen.

Als Teil des Schecks berechne ich die Größe des Datentyp -Bereichs für jeden IDENTITY Säule; Ich benutze das, um zu berechnen, wie viel Prozent dieses Bereichs erschöpft waren. Zum DECIMAL und NUMERIC Die Größe dieses Bereichs ist 2 * 10^p - 2 wo p ist die Präzision.

Ich habe eine Reihe von Testtabellen mit erstellt DECIMAL und NUMERIC IDENTITY Spalten und versuchten, ihre 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 schränkte es auf die IDENTITY Spalten vom Typ DECIMAL(38, 0) (dh mit der maximalen Präzision), also habe ich dann das ausprobiert POWER() Berechnung direkt auf diesen Wert.

Alle folgenden Fragen

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 zu konvertieren? POWER(), was vom Typ ist FLOAT, zu NUMERIC (besonders wenn FLOAT hat einen höheren Vorrang)?
  • Wie kann ich den Bereich von a dynamisch berechnen? DECIMAL oder NUMERIC Spalte für alle möglichen Präzisionen (einschließlich p = 38, Natürlich)?
War es hilfreich?

Lösung

Von dem POWER Dokumentation:

Syntax

POWER ( float_expression , y )

Argumente

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

y
Ist die Macht, die er erhöhen kann float_expression. y kann ein Ausdruck der exakten numerischen oder ungefähren numerischen Datentypkategorie sein, mit Ausnahme der bisschen Datentyp.

Rückgabetypen

Gibt den gleichen Typ zurück wie eingereicht in float_expression. Zum Beispiel wenn a Dezimal(2,0) wird als float_expression eingereicht, das zurückgegebene Ergebnis ist Dezimal(2,0).


Der erste Eingang wird implizit auf float im Bedarfsfall.

Die interne Berechnung wird mit Verwendung durchgeführt float Arithmetik nach der Standardfunktion Standard C Runtime Library (CRT) pow.

Das float Ausgabe von pow wird dann zurück zum Typ des linken Operanden (impliziert, um es zu sein numeric(3,1) Wenn Sie den wörtlichen Wert 10.0 verwenden).

Mit einem expliziten float Funktioniert in Ihrem Fall gut:

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

Ein genaues Ergebnis für 1038 kann nicht auf einem SQL -Server gespeichert werden decimal/numeric weil es 39 Ziffern Präzision erfordern würde (1 gefolgt von 38 Nullen). Die maximale Präzision beträgt 38.

Andere Tipps

Anstatt sich mit Martins Antwort weiter einzumischen, werde ich den Rest meiner Erkenntnisse in Bezug auf die Erkenntnisse hinzufügen POWER() hier.

Halten Sie Ihre Schlüpfer fest.

Präambel

Erstens präsentiere ich Ihnen, Ausstellung A,, die MSDN -Dokumentation für POWER():

Syntax

POWER ( float_expression , y )

Argumente

float_expressionIst ein Ausdruck des Typs des Typs oder eines Typs, der implizit in Schwimmer konvertiert werden kann.

Rückgabetypen

Gleich wie float_expression.

Sie können aus dem Lesen dieser letzten Zeile das abschließen POWER()Der Rückgabetyp ist FLOAT, aber lesen Sie noch einmal. float_expression ist "vom Typ Float oder eines Typs, der implizit in Schwimmer konvertiert werden kann". Also trotz seines Namens, float_expression kann eigentlich ein sein FLOAT, a DECIMAL, oder an INT. Seit der Ausgabe von POWER() ist dasselbe wie das von float_expression, Es kann auch einer dieser Typen sein.

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

Beobachtungen

Ich präsentiere dir, Anlage B, ein Test, der das demonstriert, das POWER() leitet seine Ausgabe je nach Eingabe auf verschiedene Datentypen aus. Bitte lesen Sie dieses Skript mit einem faux -jamaikanischen Akzent für maximal abgeleitete Vorteile.

SELECT 
    POWER(10, 3)             AS int_man
  , POWER(1000000000000, 3)  AS numeric0_man     -- one trillion
  , POWER(10.0, 3)           AS numeric1_man
  , POWER(10.12305, 3)       AS numeric5_man
  , POWER(1e1, 3)            AS float_man
INTO power_test_man;

EXECUTE sp_help power_test_man;

DROP TABLE power_test_man;

Die relevanten Ergebnisse sind:

Column_name    Type      Length    Prec     Scale
-------------------------------------------------
int_man        int       4         10       0
numeric0_man   numeric   17        38       0
numeric1_man   numeric   17        38       1
numeric5_man   numeric   17        38       5
float_man      float     8         53       NULL

Was scheint, ist das POWER() Abgüsse float_expression in den kleinsten Typ, der zu ihm passt, nicht einbezogen BIGINT.

Deswegen, SELECT POWER(10.0, 38); versagt mit einem Überlauffehler, weil 10.0 wird gegossen NUMERIC(38, 1) Was ist nicht groß genug, um das Ergebnis von 10 zu halten38. Das liegt daran, dass 1038 erweitert sich um 39 Ziffern vor der Dezimalzahl, während NUMERIC(38, 1) kann 37 Ziffern vor dem Dezimal und eins danach speichern. Daher der Maximalwert NUMERIC(38, 1) kann halten ist 1037 - 0.1.

Mit diesem Verständnis kann ich einen weiteren Überlaufversagen wie folgt zusammenfassen.

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

Eine Milliarde (im Gegen NUMERIC(38, 0)) ist gerade klein genug, um in eine zu passen INT. Eine Milliarde auf die dritte Macht erhoben ist jedoch zu groß für INT, daher der Überlauffehler.

Mehrere andere Funktionen weisen ein ähnliches Verhalten auf, wobei ihr Ausgangstyp von ihrer Eingabe abhängt:

  • Mathematische Funktionen: POWER(), CEILING(), FLOOR(), RADIANS(), DEGREES(), und ABS()
  • Systemfunktionen und Ausdrücke: NULLIF(), ISNULL(), COALESCE(), IIF(), CHOOSE(), und CASE Ausdrücke
  • Rechenzeichen: Beide SELECT 2 * @MAX_INT; und SELECT @MAX_SMALLINT + @MAX_SMALLINT;, Zum Beispiel führen Sie zu arithmetischen Überläufen, wenn die Variablen vom benannten Datentyp sind.

Fazit

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

Da diese Funktionen so alltäglich sind, ist es wichtig zu verstehen, dass Ihre Ergebnisse aufgrund ihres Verhaltens zu Überlauffehlern führen können. Wenn Sie einen bestimmten Datentyp für Ihre Ausgabe erwarten oder sich auf einen bestimmten Datentyp verlassen, geben Sie die entsprechende Eingabe nach Bedarf explizit an.

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

Ja Mann.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit dba.stackexchange
scroll top