標籤:style blog http color io 使用 ar for strong
解剖SQLSERVER 第五篇 OrcaMDF裡讀取Bits類型資料(譯)
http://improve.dk/reading-bits-in-orcamdf/
Bits類型的儲存跟SQLSERVER其他定長資料類型的儲存很不一樣。通常,所有定長列都會顯示出來,一個條記錄裡定長資料部分的欄位資料總是一個挨著一個
我們可以寫入磁碟的最小資料單位是一個位元組,儲存位類型資料的天真的方法就是使用一整個(位元組@)來儲存每一個位,使用常用的格式去解釋位類型資料是很簡單的
,不過這會浪費一些空間 ,就像null位元影像,如果一個表只有3列,那麼用一個位元組來儲存null位元影像會比較浪費,因為其他的5個位都沒有用到
@:文章裡是用位 ,這裡應該是用位元組吧
在記錄的內部位類型是如何儲存的?
一些位類型列的值是儲存在一個位元組中的,最大可以到8個位,通常,我們會有如下表定義
CREATE TABLE BitTest( A bit B bit C bit D int)
記錄的定長部分資料需要佔用5個位元組,4個位元組儲存int 列 ,而另一個位元組儲存A 、B、C這三列位類型的資料,只用了位元組裡面的3個位
我們再添加一些列
CREATE TABLE BitTest( A bit B bit C bit D int E bit F bit G bit H smallint I bit J bit K bit)
E到G列按道理來說應該儲存在D列的後面,但是他們會繼續使用第一個 bit byte,直到第一個 bit byte使用完所有的位空間為止
下面的圖顯示了H列(smallint )直接儲存在D列的後面,而在D列後面是儲存K列的新bit byte,因為第一個bit byte已經滿了
當讀取行記錄裡的位類型時我們需要知道的狀態
很明顯,我們一次不能唯讀取一個欄位的值,我們讀取固定長度資料類型的時候還需要讀取定長資料位移指標
我們需要一些能在讀取的時候指示我們當前讀取到位元組中哪一個位屬於哪一個欄位的狀態,然後我們讀取一個新的bit byte
我來介紹一下RecordReadState類
public class RecordReadState{ // We start out having consumed all bits as none have been read private int currentBitIndex = 8; private byte bits; public void LoadBitByte(byte bits) { this.bits = bits; currentBitIndex = 0; } public bool AllBitsConsumed { get { return currentBitIndex == 8; } } public bool GetNextBit() { return (bits & (1 << currentBitIndex++)) != 0; }}
RecordReadState 類當前只需要處理bits,但是將來我可能還要建立一個BitReadState 類用來儲存讀取狀態
RecordReadState 類儲存了一個位元組用來當作指標指出下一個可用的位在位元組的哪個地方,如果位元組已經用完了儲存滿了所有的位元據
(currentBixIndex = 8 (0-7 being the available bits)),方法AllBitsConsumed 就會返回true,指示我們需要讀取一個新的 bit byte
GetNextBit方法只是簡單的從 bit byte中讀取當前的bit ,然後將currentBitIndex(bit index)的值加1
demo
using NUnit.Framework;using OrcaMDF.Core.Engine.Records;namespace OrcaMDF.Core.Tests.Engine.Records{ [TestFixture]public class RecordReadStateTests{ [Test]public void General(){var state = new RecordReadState();// No bits availableAssert.IsTrue(state.AllBitsConsumed);state.LoadBitByte(0xD2); // 11010010// Bits availableAssert.IsFalse(state.AllBitsConsumed);// Reading bit valuesAssert.IsFalse(state.GetNextBit());Assert.IsTrue(state.GetNextBit());Assert.IsFalse(state.GetNextBit());Assert.IsFalse(state.GetNextBit());Assert.IsTrue(state.GetNextBit());Assert.IsFalse(state.GetNextBit());Assert.IsTrue(state.GetNextBit());// One bit leftAssert.IsFalse(state.AllBitsConsumed);Assert.IsTrue(state.GetNextBit());// Bits exhausted, ready for next byteAssert.IsTrue(state.AllBitsConsumed);}}}
SqlBit實現
一旦我們實現了狀態的讀取,我們就可以實現SqlBit 類型
public class SqlBit : ISqlType{ private readonly RecordReadState readState; public SqlBit(RecordReadState readState) { this.readState = readState; } public bool IsVariableLength { get { return false; } } public short? FixedLength { get { if (readState.AllBitsConsumed) return 1; return 0; } } public object GetValue(byte[] value) { if(readState.AllBitsConsumed && value.Length != 1) throw new ArgumentException("All bits consumed, invalid value length: " + value.Length); if (value.Length == 1) readState.LoadBitByte(value[0]); return readState.GetNextBit(); }}
SqlBit 在建構函式裡傳入一個read state,read state指示目前記錄讀取操作的範圍。需要注意的是固定長度需要依據read state裡的當前AllBitsConsumed值
如果位元組裡面所有位都被佔用,那麼意味著需要讀取整個位元組,如果if (readState.AllBitsConsumed)返回0表示不需要讀取整個位元組,但是GetValue方法依然會被調用
GetValue方法會驗證一種情況:readState.AllBitsConsumed 返回真,證明 bit byte是有資料存放區在裡面,但是value.Length返回的長度是0,那證明有問題了
如果我們讀到一個值,我們會請求 read state 去裝載一個新的bit byte ,之後,我們可以調用GetNextBit 方法返回 read state的當前bit
相關測試
using NUnit.Framework;using OrcaMDF.Core.Engine.Records;using OrcaMDF.Core.Engine.SqlTypes;namespace OrcaMDF.Core.Tests.Engine.SqlTypes{ [TestFixture] public class SqlBitTests { [Test] public void GetValue() { var readState = new RecordReadState(); var type = new SqlBit(readState); // No bytes read - length is one Assert.AreEqual(1, type.FixedLength); // Load byte and check length is 0 readState.LoadBitByte(0xD2); Assert.AreEqual(0, type.FixedLength); Assert.IsFalse((bool)type.GetValue(new byte[0])); Assert.IsTrue((bool)type.GetValue(new byte[0])); Assert.IsFalse((bool)type.GetValue(new byte[0])); Assert.IsFalse((bool)type.GetValue(new byte[0])); Assert.IsTrue((bool)type.GetValue(new byte[0])); Assert.IsFalse((bool)type.GetValue(new byte[0])); Assert.IsTrue((bool)type.GetValue(new byte[0])); // One bit left - length should still be 0 Assert.AreEqual(0, type.FixedLength); Assert.IsTrue((bool)type.GetValue(new byte[0])); // All bits consumed - length should be 1 Assert.AreEqual(1, type.FixedLength); } }}
第五篇完
解剖SQLSERVER 第五篇 OrcaMDF裡讀取Bits類型資料(譯)