Ich stoße auf ein Problem, bei dem ein gültiger numerischer Wert meine Einfügeanweisung mühsam blockiert. Eine manuelle SQL-Einfügung in die Datenbank mit dem gleichen Zahlenwert funktioniert.
Manuelle SQL-Einfügung:
INSERT INTO foo (id, foo_id, timestamp, value, value2)
VALUES ('ff3bd5e2-dbd8-424b-bb4f-455df1fe9f9c', 'b3a0a2c6-3b58-4659-8b4f-9466cede43be',
'2016-11-01T00:00:00+00:00', null, 66666.55);
Hier ist mein Code:
const bluebird = require('bluebird');
const sql = require('mssql');
const db = require('./database.js');
sql.Promise = bluebird;
const COLUMNS = {
id: 'id',
foo_id: 'foo_id',
timestamp: 'timestamp',
value: 'value',
value2: 'value2'
};
exports.create = data => {
return db.getConnection()
.then(conn => {
const ps = new sql.PreparedStatement(conn);
ps.input(COLUMNS.id, sql.UniqueIdentifier);
ps.input(COLUMNS.foo_id, sql.UniqueIdentifier);
ps.input(COLUMNS.timestamp, sql.DateTimeOffset(0));
ps.input(COLUMNS.value, sql.Numeric(30, 15));
ps.input(COLUMNS.value2, sql.Numeric(30, 15));
// Have to convert string to JavaScript date for insert to work
data.timestamp = new Date(data.timestamp);
const header = Object.keys(COLUMNS).map(key => COLUMNS[key]).join();
const query = `INSERT INTO foo (${header}) VALUES (@${COLUMNS.id},` +
` @${COLUMNS.foo_id}, @${COLUMNS.timestamp}, @${COLUMNS.value},` +
` @${COLUMNS.value2})`;
return ps.prepare(query)
.then(() => ps.execute(data)
// Releases the connection back to the pool
.then(() => ps.unprepare()))
.then(() => bluebird.resolve());
});
};
Der mühsame Code scheint 66666.55 in eine ganze Zahl mit einem Wert von 66666550000000000000 zu konvertieren. Er versucht dann, den Rest und den Quotienten dieses Wertes zu nehmen und ihn mit writeUInt32LE in einen Puffer zu schreiben.
Warum wandelt der mühsame Code Gleitkommazahlen in Ganzzahlen um und schreibt sie in Puffer, anstatt die Gleitkommazahl beizubehalten und die integrierte Funktion writeDoubleLE für Puffer zu verwenden?
Dieses Problem hindert mich daran, weiterzumachen.
Warum wandelt der mühsame Code Gleitkommazahlen in ganze Zahlen um ...
Denn das fordert das TDS-Protokoll für numerische Werte.
2.2.5.5.1.6 Dezimal/Numerisch
Für sql.Numeric(30, 15)
, _p_ ist 30 und _s_ ist 15. Die ganze Zahl ist also 66666550000000000000 (66666,55 * 10^15) und sollte in 16 Bytes dargestellt werden.
...
buffer.writeUInt64LE(value); // 8 bytes
buffer.writeUInt32LE(0x00000000); // 4 bytes
buffer.writeUInt32LE(0x00000000); // 4 bytes
Die erste Zeile ist die mit dem Problem.
https://github.com/tediousjs/tedious/blob/master/src/tracking-buffer/writable-tracking-buffer.js#L125
Es gibt keinen Buffer.writeInt64LE, daher muss dies in zwei 32-Bit-Chunks erfolgen.
writeUInt64LE(value) {
this.writeInt32LE(value & -1);
return this.writeUInt32LE(Math.floor(value * SHIFT_RIGHT_32));
}
Bei der 32-Bit-Rechtsverschiebung scheint sich das Problem zu manifestieren.
Math.floor(66666550000000000000 * SHIFT_RIGHT_32)
ergibt 15522015746
was 0x39D2F2A02
. Das erfordert 34 Bit.
Jetzt sehe ich, dass das Problem darin besteht, dass 66666550000000000000 0x39d2f2a02aed16000 ist, was mehr als 64 Bit erfordert. Dies wirft jedoch das Problem auf, dass nicht alle Ganzzahlen mit mehr als 53 Bit im _Number_-Typ von JavaScripts korrekt dargestellt werden können. Um dies richtig zu handhaben, sind möglicherweise einige Kopfzerbrechen und einige neue Unit-Tests erforderlich.
Als Workaround würde die Verwendung eines kleineren Maßstabs wahrscheinlich funktionieren.
@pekim Danke für die Beantwortung meiner Frage und die Bereitstellung der Informationen! Ich beschloss, es in Python statt in Node.js zu schreiben, um voranzukommen. Ich werde diese Bibliothek weiterhin für andere Arbeiten verwenden, aber es werden keine numerischen Typen verwendet. Hoffentlich kann eine Lösung gefunden werden, damit niemand anderes auf dieses Problem stößt.
In meinem Fall musste ich einen anderen Workaround verwenden, da Numeric
von einer Bibliothek auf Tedious (Sequelize) eingerichtet wurde. Im Wesentlichen formatiere ich den Parameter so, dass die Dezimalkonvertierung innerhalb der Datenbank und nicht im Code erfolgt. Für alle Zahlen muss ich dies verwenden:
const value_stored = value.toFixed(desired_precision);
Dies dient dazu, dass Sie nicht an Präzision verlieren, aber auch nichts zwischen meinem Code und der DB verstopfen, das Annahmen über Maßstab und Größe macht, die ich nicht kenne oder kontrolliere. Natürlich ist dies weniger bandbreiteneffizient als die Verwendung des richtigen Typs, aber in meinem Fall ist eine Strafe von <30 Byte pro Datensatz unbedeutend.
In meinem Fall musste ich einen anderen Workaround verwenden, da
Numeric
von einer Bibliothek auf Tedious (Sequelize) eingerichtet wurde. Im Wesentlichen formatiere ich den Parameter so, dass die Dezimalkonvertierung innerhalb der Datenbank und nicht im Code erfolgt. Für alle Zahlen muss ich dies verwenden:const value_stored = value.toFixed(desired_precision);
Dies dient dazu, dass Sie nicht an Präzision verlieren, aber auch nichts zwischen meinem Code und der DB verstopfen, das Annahmen über Maßstab und Größe macht, die ich nicht kenne oder kontrolliere. Natürlich ist dies weniger bandbreiteneffizient als die Verwendung des richtigen Typs, aber in meinem Fall ist eine Strafe von <30 Byte pro Datensatz unbedeutend.
Das hat das Problem bei mir auch gelöst.
Gibt es eine Möglichkeit, dies global anzuwenden, damit die Fortsetzung jedes Mal versucht, einen Dezimalwert einzufügen / zu aktualisieren, der .toFixed (2) aufruft?
Hilfreichster Kommentar
In meinem Fall musste ich einen anderen Workaround verwenden, da
Numeric
von einer Bibliothek auf Tedious (Sequelize) eingerichtet wurde. Im Wesentlichen formatiere ich den Parameter so, dass die Dezimalkonvertierung innerhalb der Datenbank und nicht im Code erfolgt. Für alle Zahlen muss ich dies verwenden:Dies dient dazu, dass Sie nicht an Präzision verlieren, aber auch nichts zwischen meinem Code und der DB verstopfen, das Annahmen über Maßstab und Größe macht, die ich nicht kenne oder kontrolliere. Natürlich ist dies weniger bandbreiteneffizient als die Verwendung des richtigen Typs, aber in meinem Fall ist eine Strafe von <30 Byte pro Datensatz unbedeutend.