2016-10-16 86 views
2

我對.net中的Sqlite沒有太多的經驗,但我看到的行爲很奇怪。比方說,我們有以下project.json一個.net核心應用:小數類型從內存中的Sqlite數據庫讀取爲double或int

{ 
    "version": "1.0.0-*", 
    "buildOptions": { 
    "debugType": "portable", 
    "emitEntryPoint": true 
    }, 
    "dependencies": { 
    "Microsoft.Data.Sqlite": "1.0.0", 
    "Dapper": "1.50.2" 
    }, 
    "frameworks": { 
    "netcoreapp1.0": { 
     "dependencies": { 
     "Microsoft.NETCore.App": { 
      "type": "platform", 
      "version": "1.0.0" 
     } 
     }, 
     "imports": "dnxcore50" 
    } 
    } 
} 

另外,我們有一個簡單的類Item

public class Item 
{ 
    public Item() { } 

    public Item(int id, string name, decimal price) 
    { 
     this.Id = id; 
     this.Name = name; 
     this.Price = price; 
    } 

    public int Id { get; set; } 
    public string Name { get; set; } 
    public decimal Price { get; set; } 
} 

然後,我創建的內存數據庫和填充數據(使用小巧玲瓏):

var connection = new SqliteConnection("Data Source=:memory:"); 
connection.Open(); 
connection.Execute("CREATE TABLE IF NOT EXISTS Items(Id INT, Name NVARCHAR(50), Price DECIMAL)"); 

var items = new List<Item> 
{ 
    new Item(1, "Apple", 3m), 
    new Item(2, "Banana", 1.4m) 
}; 
connection.Execute("INSERT INTO Items(Id, Name, Price) VALUES (@Id, @Name, @Price)", items); 

然後我嘗試從Items表如下:

var dbItems = connection.Query<Item>("SELECT Id, Name, Price FROM Items").ToList(); 

當運行該溶液中,得到類似如下例外:

未處理異常:System.InvalidOperationException:錯誤解析 第2列(價格= 1.4 - 雙)---> System.Invali dCastException : 無法投射「System.Double」類型的對象以鍵入「System.Int64」。

好吧,那我就用Microsoft.Data.Sqlite獲取數據:

var command = connection.CreateCommand(); 
command.CommandText = "SELECT Price FROM Items"; 
var reader = command.ExecuteReader(); 
while (reader.Read()) 
{ 
    Console.WriteLine(reader[0].GetType()); 
} 

結果我得到:

System.Int64 // Price = 3                           
System.Double // Price = 1.4 

我試圖運行與小數的價格真正的數據庫查詢,返回的數據類型是正確的,並且總是十進制的(如預期的那樣)。
我應該進一步挖掘什麼方向?我的內存數據庫有問題嗎?如何使其與小數一致?

回答

3

這是SQLite的一個功能,請訪問以下鏈接:https://sqlite.org/faq.html#q3

+0

哇,我會很高興,感謝您的鏈接,會讀。那麼這是否適用於插入和選擇?我確信,當我使用一個真正的數據庫文件,並在那裏使用了一個「decimal」字段時,它按預期的方式被讀取(如十進制),沒有錯誤。我現在很困惑...... –

+0

SQLite會將您示例中的第一個值存儲爲「3」,而不是「3.0」或「3m」。當它讀取它時,由於動態類型是假設值是一個int。真正的數據庫具有強類型數據,因此它不做任何假設,並將其作爲十進制返回。 – bib1257

4

在SQLite的,如果你創建一個數字/十進制列數據類型的表,它關聯到數字型親和力和它的默認行爲是如果值沒有小數則存儲整數,或者如果值包含小數則存儲實數。這有助於節省字節的存儲空間,因爲最常用的整數值(小於爲小到中等的應用程序一百萬值)需要比實際數據種類較少字節:

整數(約):

  • ±128> 1個字節(2^8-1)
  • ±32,768> 2個字節(2^16-1)
  • ±8388608> 3個字節(2^24-1)
  • ±2147483648> 4個字節(2^32-1)
  • ±140,737,488,355,328> 6字節(2^48-1)
  • ±9,223,372,036,854,780,000> 8個字節(2^64-1)

針對8字節實IEEE數據類型

如果你需要存儲一個實數始終,有必要聲明浮點值/實際數據類型以被認爲是真正的類型親和力,因此即使你發送一個整數到SQLite的,將被存儲爲真正

來源:https://sqlite.org/datatype3.html

我知道真正的可能是不準確的,但我做了一些在wal模式下使用C#Decimal數據類型和SQLite與磁盤數據庫進行測試,如果所有操作都是在C#中完成的,我從來沒有發生過舍入錯誤。但是當我直接在SQLite中進行了一些計算時,由於2整數除法,我得到了一些舍入誤差和一些不好的計算結果

我使用EF6和Dapper讀取/寫入數值(SQLite中的整數/實數) 。現在,我打算利用SQLite數字行爲來節省磁盤空間,使用小型的TypeHandler,因此我可以複製SQL Server Money實現(在內部,SQL Server保存一個8字節的整數,當它被10000除時)加載到內存中):

public class NumericTypeHandler : SqlMapper.TypeHandler<decimal> 
{ 
    public override void SetValue(IDbDataParameter parameter, decimal value) 
    { 
     parameter.Value = (long)(value/10000); 
    } 

    public override decimal Parse(object value) 
    { 
     return ((decimal)value)/10000M; 
    } 
} 

此外,我還設置了日期時間UnixEpoch實施,以節省磁盤空間並提高性能

注:

的SQLite可以處理幾十個用戶在低需求asp.net應用程序,訣竅是用編譯指示指令和其他東西熱身SQLite的:

  • WAL模式:有助於更好地管理併發性和性能
  • 頁面大小:如果文件系統的簇大小相匹配,提高了性能
  • 共享緩存模式:當它在多個線程
  • 日期時間UnixEpoch真實加入實現表鎖,而不是數據庫鎖:保存秒(4個字節),而不是文本ISO日期時間( 「2012-12-21T17:30:24」= 20字節)。
  • 緩存容量:存儲I/O訪問保持上次訪問的內存頁

我也做在C#中端一些改進:

  • 我做了一個自定義DateTime結構 DATETIME值爲了將數據保存爲整型覆蓋ToString方法並作爲帶有構造函數重載的DateTime加載到內存中。
  • 我在C#中做了一個自定義數字結構,(如日期時間一個),省錢爲美分重寫ToString方法,並加載到內存中,與構造函數重載十進制

避免節省了不必要的數據(錯誤日誌,電子郵件html text)

我想你應該使用像EFx或Dapper這樣的ORM,如果你打算使用SQLite作爲後端。使用精簡版,您需要創建自己的微型框架以節省開發時間(基本CRUD功能和Linq查詢轉換)。

我發現的最好的工具是https://github.com/linq2db/linq2db,你用linq來訪問數據庫,速度和linq都很好。並且可以使CRUD功能。

如果這個答案幫助

問候

+0

我忘了說我已經讀過net core在生產環境中不太成熟,還有很多bug還沒有解決 – Molem

相關問題