Tedious: Nilai di luar batas untuk tipe Numerik

Dibuat pada 31 Okt 2016  ·  5Komentar  ·  Sumber: tediousjs/tedious

Saya mengalami masalah di mana nilai numerik yang valid memblokir pernyataan penyisipan saya dengan membosankan. Penyisipan SQL manual ke dalam database dengan nilai numerik yang sama berfungsi.

Sisipan SQL Manual:

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);

Berikut kode saya:

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());
    });
};

Kode yang membosankan tampaknya mengubah 66666.55 menjadi bilangan bulat dengan nilai 66666550000000000000. Kemudian mencoba mengambil sisa dan hasil bagi dari nilai itu dan menulisnya ke buffer menggunakan writeUInt32LE.

Mengapa kode yang membosankan mengubah angka floating point menjadi bilangan bulat dan menulisnya ke buffer alih-alih menyimpan angka floating point dan menggunakan fungsi bawaan writeDoubleLE untuk buffer?

Masalah ini menghalangi saya untuk bergerak maju.

known issue

Komentar yang paling membantu

Dalam kasus saya, saya harus menggunakan solusi yang berbeda karena Numeric sedang disiapkan oleh perpustakaan di atas Tedious (Sequelize). Intinya, saya memformat parameter sedemikian rupa sehingga konversi desimal terjadi di dalam database daripada dalam kode. Untuk semua nomor, saya harus menggunakan ini:

const value_stored = value.toFixed(desired_precision);

Ini agar Anda tidak kehilangan presisi tetapi juga tidak mencekik apa pun di antara kode saya dan DB yang membuat asumsi tentang skala dan besarnya yang tidak saya sadari atau kendalikan. Tentu saja, ini kurang efisien bandwidth daripada menggunakan tipe yang benar tetapi dalam kasus saya <30 byte per record penalti tidak signifikan.

Semua 5 komentar

Mengapa kode yang membosankan mengubah angka floating point menjadi bilangan bulat ...

Karena itulah yang diamanatkan oleh protokol TDS untuk nilai Numerik.
2.2.5.5.1.6 Desimal/Numerik

Untuk sql.Numeric(30, 15) , _p_ adalah 30, dan _s_ adalah 15. Jadi bilangan bulatnya adalah 66666550000000000000 (66666.55 * 10^15), dan harus direpresentasikan dalam 16 byte.

https://github.com/tediousjs/tedious/blob/3cb8586c2be217d9b31ef72f866986d85614920d/src/data-type.js#L519

  ...
  buffer.writeUInt64LE(value);        // 8 bytes
  buffer.writeUInt32LE(0x00000000);   // 4 bytes
  buffer.writeUInt32LE(0x00000000);   // 4 bytes

Baris pertama adalah yang bermasalah.

https://github.com/tediousjs/tedious/blob/master/src/tracking-buffer/writable-tracking-buffer.js#L125
Tidak ada Buffer.writeInt64LE, jadi itu harus dilakukan dalam dua potongan 32 bit.

 writeUInt64LE(value) {
    this.writeInt32LE(value & -1);
    return this.writeUInt32LE(Math.floor(value * SHIFT_RIGHT_32));
  }

Pergeseran kanan 32 bit adalah tempat masalah tampaknya memanifestasikan dirinya.
Math.floor(66666550000000000000 * SHIFT_RIGHT_32) menghasilkan 15522015746 yaitu 0x39D2F2A02 . Itu membutuhkan 34 bit.

Sekarang saya melihat bahwa masalahnya adalah 66666550000000000000 adalah 0x39d2f2a02aed16000, yang membutuhkan lebih dari 64 bit. Tetapi ini menimbulkan masalah bahwa tidak semua bilangan bulat dengan lebih dari 53 bit dapat direpresentasikan dengan benar dalam tipe _Number_ JavaScripts. Untuk menangani ini dengan benar mungkin memerlukan beberapa goresan kepala, dan beberapa tes unit baru.

Sebagai solusinya, menggunakan skala yang lebih kecil mungkin akan berhasil.

@pekim Terima kasih telah menjawab pertanyaan saya dan memberikan informasi! Saya memutuskan untuk menulisnya dengan Python alih-alih Node.js untuk bergerak maju. Saya masih akan menggunakan perpustakaan ini untuk pekerjaan lain, tetapi tidak akan melibatkan tipe numerik. Semoga solusi dapat ditemukan sehingga tidak ada orang lain yang mengalami masalah ini.

Dalam kasus saya, saya harus menggunakan solusi yang berbeda karena Numeric sedang disiapkan oleh perpustakaan di atas Tedious (Sequelize). Intinya, saya memformat parameter sedemikian rupa sehingga konversi desimal terjadi di dalam database daripada dalam kode. Untuk semua nomor, saya harus menggunakan ini:

const value_stored = value.toFixed(desired_precision);

Ini agar Anda tidak kehilangan presisi tetapi juga tidak mencekik apa pun di antara kode saya dan DB yang membuat asumsi tentang skala dan besarnya yang tidak saya sadari atau kendalikan. Tentu saja, ini kurang efisien bandwidth daripada menggunakan tipe yang benar tetapi dalam kasus saya <30 byte per record penalti tidak signifikan.

Dalam kasus saya, saya harus menggunakan solusi yang berbeda karena Numeric sedang disiapkan oleh perpustakaan di atas Tedious (Sequelize). Intinya, saya memformat parameter sedemikian rupa sehingga konversi desimal terjadi di dalam database daripada dalam kode. Untuk semua nomor, saya harus menggunakan ini:

const value_stored = value.toFixed(desired_precision);

Ini agar Anda tidak kehilangan presisi tetapi juga tidak mencekik apa pun di antara kode saya dan DB yang membuat asumsi tentang skala dan besarnya yang tidak saya sadari atau kendalikan. Tentu saja, ini kurang efisien bandwidth daripada menggunakan tipe yang benar tetapi dalam kasus saya <30 byte per record penalti tidak signifikan.

Itu memecahkan masalah bagi saya juga.

Apakah ada cara untuk menerapkan ini secara global sehingga setiap sekuel mencoba memasukkan/memperbarui nilai desimal yang disebut .toFixed(2)?

Apakah halaman ini membantu?
0 / 5 - 0 peringkat