好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

解剖SQLSERVER第五篇OrcaMDF里读取Bits类型数据(译)

解剖SQLSERVER 第五篇 OrcaMDF里读取Bits类型数据(译) http://improve.dk/reading-bits-in-orcamdf/ Bits类型的存储跟SQLSERVER其他定长数据类型的存储很不一样。通常,所有定长列都会显示出来,一个条记录里定长数据部分的字段数据总是一个挨着一个 我们

解剖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   0  ;
    }
}  

RecordReadState 类当前只需要处理bits,但是将来我可能还要创建一个BitReadState 类用来保存读取状态

RecordReadState 类保存了一个字节用来当作指针指出下一个可用的位在字节的哪个地方,如果字节已经用完了存储满了所有的位数据

(currentBixIndex = 8 (0-7 being the available bits)),方法AllBitsConsumed 就会返回true,指示我们需要读取一个新的 bit byte

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 available 
 Assert.IsTrue(state.AllBitsConsumed);
state.LoadBitByte(  0xD2 );  //   11010010
  //   Bits available 
 Assert.IsFalse(state.AllBitsConsumed);
  //   Reading bit values 
 Assert.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 left 
 Assert.IsFalse(state.AllBitsConsumed);
Assert.IsTrue(state.GetNextBit());
  //   Bits exhausted, ready for next byte 
 Assert.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类型数据(译)的详细内容...

  阅读:38次