解剖SQLSERVER 第四篇 OrcaMDF里对dates类型数据的解析(译)

解剖SQLSERVER 第四篇  OrcaMDF里对dates类型数据的解析(译)

http://improve.dk/parsing-dates-in-orcamdf/

在SQLSERVER里面有几种不同的date相关类型,当前OrcaMDF 支持三种最常用的date类型:date,datetime,smalldatetime

SqlDate实现

date 类型在三种类型之中是最简单的,他是一个3个字节的定长类型,存储了日期值它支持的日期范围从0001-01-01到9999-12-31

默认值是1900-01-01

比较坑爹的是.NET里面还没有任何标准实现能够支持3个字节的整数类型,只有short类型和int类型,但是,他们要不太大要不太小

另外,要正确读取日期值,对于.NET的4字节整型我们必须执行一些转变去获取正确的数字

一旦我们获取到date的值,我们可以创建一个默认的datetime类型并且添加天数进去

public class SqlDate : ISqlType
{
    public bool IsVariableLength
    {
        get { return false; }
    }

    public short? FixedLength
    {
        get { return 3; }
    }

    public object GetValue(byte[] value)
    {
        if (value.Length != 3)
            throw new ArgumentException("Invalid value length: " + value.Length);

        // Magic needed to read a 3 byte integer into .NET‘s 4 byte representation.
        // Reading backwards due to assumed little endianness.
        int date = (value[2] << 16) + (value[1] << 8) + value[0];

        return new DateTime(1, 1, 1).AddDays(date);
    }
}

相关测试

using System;
using NUnit.Framework;
using OrcaMDF.Core.Engine.SqlTypes;
namespace OrcaMDF.Core.Tests.Engine.SqlTypes
{
    [TestFixture]
public class SqlDateTests
{
        [Test]
public void GetValue()
{
var type = new SqlDate();
var input = new byte[] { 0xf6, 0x4c, 0x0b };
Assert.AreEqual(new DateTime(2028, 09, 09), Convert.ToDateTime(type.GetValue(input)));
input = new byte[] { 0x71, 0x5c, 0x0b };
Assert.AreEqual(new DateTime(2039, 07, 17), Convert.ToDateTime(type.GetValue(input)));
}
        [Test]
public void Length()
{
var type = new SqlDate();
Assert.Throws<ArgumentException>(() => type.GetValue(new byte[2]));
Assert.Throws<ArgumentException>(() => type.GetValue(new byte[4]));
}
}
}

SqlDateTime实现

date类型只能存储日期,而datetime类型不但能存储date也能存储time

datetime存储8字节定长数据值,第一部分是time(4字节),而第二部分是date(4字节)

计算date部分跟上面介绍date类型基本上一样,不过这一次date部分是一个四字节整数,比上面的例子容易处理多了,上面的date类型是3个字节

time部分存储为自午夜时的ticks数,一个tick就是1/300th 秒,为了显示tick值,我们首先定义一个常量,常量值是10d/3d

time的各个部分实际同样存储在同一个整型值里面(比如时间,分钟,秒,毫秒),所以我们要独立访问这些单独的部分,我们必须

要执行一些转换 (包括取模和相除)

部分     计算
小时   X / 300 / 60 / 60
分钟   X / 300 / 60 % 60
秒     X / 300 % 60
毫秒   X % 300 * 10d / 3d
public class SqlDateTime : ISqlType
{
    private const double CLOCK_TICK_MS = 10d/3d;

    public bool IsVariableLength
    {
        get { return false; }
    }

    public short? FixedLength
    {
        get { return 8; }
    }

    public object GetValue(byte[] value)
    {
        if (value.Length != 8)
            throw new ArgumentException("Invalid value length: " + value.Length);

        int time = BitConverter.ToInt32(value, 0);
        int date = BitConverter.ToInt32(value, 4);

        return new DateTime(1900, 1, 1, time/300/60/60, time/300/60%60, time/300%60, (int)Math.Round(time%300*CLOCK_TICK_MS)).AddDays(date);
    }
}

相关测试

using System;
using NUnit.Framework;
using OrcaMDF.Core.Engine.SqlTypes;
namespace OrcaMDF.Core.Tests.Engine.SqlTypes
{
    [TestFixture]
public class SqlDateTimeTests
{
        [Test]
public void GetValue()
{
var type = new SqlDateTime();
byte[] input;
input = new byte[] { 0x5e, 0x3b, 0x5d, 0x00, 0x25, 0x91, 0x00, 0x00 };
Assert.AreEqual(new DateTime(2001, 09, 25, 05, 39, 26, 820), (DateTime)type.GetValue(input));
input = new byte[] { 0xb6, 0x87, 0xf0, 0x00, 0xd1, 0x8b, 0x00, 0x00 };
Assert.AreEqual(new DateTime(1997, 12, 31, 14, 35, 44, 607), (DateTime)type.GetValue(input));
input = new byte[] { 0x2d, 0xfd, 0x1c, 0x01, 0x4a, 0x75, 0x00, 0x00 };
Assert.AreEqual(new DateTime(1982, 03, 18, 17, 17, 36, 790), (DateTime)type.GetValue(input));
input = new byte[] { 0xff, 0x81, 0x8b, 0x01, 0x7f, 0x24, 0x2d, 0x00 };
Assert.AreEqual(new DateTime(9999, 12, 31, 23, 59, 59, 997), (DateTime)type.GetValue(input));
}
        [Test]
public void Length()
{
var type = new SqlDateTime();
Assert.Throws<ArgumentException>(() => type.GetValue(new byte[9]));
Assert.Throws<ArgumentException>(() => type.GetValue(new byte[7]));
}
}
}

SqlSmallDateTime实现

Smalldatetime 是一个不错的数据类型当你需要存储范围值内的日期值(1900~2079)并且他能精确到秒

大多数场景下,精确到秒已经足够了,在一个范围的时间间隔内和精确值不需要太精确的情况下会节省很多空间

smalldatetime 数据类型会只占用4个字节,前2个字节存储自午夜的分钟数,后2个字节存储日期,默认值是1900-1-1

处理的方法跟datetime差不多,只不过使用更小的范围

部分     计算
小时    X / 60
分钟    X % 60
public class SqlSmallDateTime : ISqlType
{
    public bool IsVariableLength
    {
        get { return false; }
    }

    public short? FixedLength
    {
        get { return 4; }
    }

    public object GetValue(byte[] value)
    {
        if (value.Length != 4)
            throw new ArgumentException("Invalid value length: " + value.Length);

        ushort time = BitConverter.ToUInt16(value, 0);
        ushort date = BitConverter.ToUInt16(value, 2);

        return new DateTime(1900, 1, 1, time / 60, time % 60, 0).AddDays(date);
    }
}

相关测试

using System;
using NUnit.Framework;
using OrcaMDF.Core.Engine.SqlTypes;
namespace OrcaMDF.Core.Tests.Engine.SqlTypes
{
    [TestFixture]
public class SqlSmallDateTimeTests
{
        [Test]
public void GetValue()
{
var type = new SqlSmallDateTime();
var input = new byte[] { 0xab, 0x02, 0x5d, 0x26 };
Assert.AreEqual(new DateTime(1926, 11, 22, 11, 23, 0), Convert.ToDateTime(type.GetValue(input)));
input = new byte[] { 0x49, 0x03, 0x99, 0x09 };
Assert.AreEqual(new DateTime(1906, 9, 24, 14, 1, 0), Convert.ToDateTime(type.GetValue(input)));
}
        [Test]
public void Length()
{
var type = new SqlSmallDateTime();
Assert.Throws<ArgumentException>(() => type.GetValue(new byte[3]));
Assert.Throws<ArgumentException>(() => type.GetValue(new byte[5]));
}
}
}

第四篇完

时间: 2024-11-05 12:29:31

解剖SQLSERVER 第四篇 OrcaMDF里对dates类型数据的解析(译)的相关文章

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

原文:解剖SQLSERVER 第五篇 OrcaMDF里读取Bits类型数据(译) 解剖SQLSERVER 第五篇  OrcaMDF里读取Bits类型数据(译) http://improve.dk/reading-bits-in-orcamdf/ Bits类型的存储跟SQLSERVER其他定长数据类型的存储很不一样.通常,所有定长列都会显示出来,一个条记录里定长数据部分的字段数据总是一个挨着一个 我们可以写入磁盘的最小数据单位是一个字节,存储位类型数据的天真的方法就是使用一整个(字节@)来存储每一

解剖SQLSERVER 第八篇 OrcaMDF 现在支持多数据文件的数据库(译)

原文:解剖SQLSERVER 第八篇 OrcaMDF 现在支持多数据文件的数据库(译) 解剖SQLSERVER 第八篇  OrcaMDF 现在支持多数据文件的数据库(译) http://improve.dk/orcamdf-now-supports-databases-with-multiple-data-files/ OrcaMDF 其中一个最新特性是支持多数据文件的数据库.这在解析上面需要作出相关的小改变,实际上大部分都是bug 修复代码 由于之前只支持单个数据文件而引起的.然而这确实需要一

解剖SQLSERVER 第七篇 OrcaMDF 特性概述(译)

原文:解剖SQLSERVER 第七篇 OrcaMDF 特性概述(译) 解剖SQLSERVER 第七篇  OrcaMDF 特性概述(译) http://improve.dk/orcamdf-feature-recap/ 时间过得真快,这已经过了大概四个月了自从我最初介绍我的宠物项目OrcaMDF. 自从项目开始到现在,OrcaMDF发生了很多变化,功能更强了,因此我想提供一个概述对目前OrcaMDF的功能的概述以及我对OrcaMDF未来的计划 页面类型 OrcaMDF 当前支持以下页面的数据完整解

解剖SQLSERVER 第十篇 OrcaMDF Studio 发布+ 特性重温(译)

原文:解剖SQLSERVER 第十篇 OrcaMDF Studio 发布+ 特性重温(译) 解剖SQLSERVER 第十篇  OrcaMDF Studio 发布+ 特性重温(译) http://improve.dk/orcamdf-studio-release-feature-recap/ 自从我上次作了一个OrcaMDF特性概述之后,两个半月过去了. 只是两个半月过去了自从我上次一个OrcaMDF特性概述.从那时起我一直在忙着参加SQLSERVER的最顶级的三个会议:SQLBits.SQLPA

解剖SQLSERVER 第六篇 对OrcaMDF的系统测试里避免regressions(译)

原文:解剖SQLSERVER 第六篇 对OrcaMDF的系统测试里避免regressions(译) 解剖SQLSERVER 第六篇  对OrcaMDF的系统测试里避免regressions (译) http://improve.dk/avoiding-regressions-in-orcamdf-by-system-testing/ 当我继续添加新功能和新的数据结构支持进去OrcaMDF软件的时候,bug的风险不断增加 特别是当我开发一个很大的未知功能时,我不能预估结构和该结构的关联,为了降低风

解剖SQLSERVER 第十三篇 Integers在行压缩和页压缩里的存储格式揭秘(译)

原文:解剖SQLSERVER 第十三篇 Integers在行压缩和页压缩里的存储格式揭秘(译) 解剖SQLSERVER 第十三篇    Integers在行压缩和页压缩里的存储格式揭秘(译) http://improve.dk/the-anatomy-of-row-amp-page-compressed-integers/ 当解决OrcaMDF对行压缩的支持的时候,视图解析整数的时候遇到了一些挑战. 和正常的未压缩整数存储不同的是这些都是可变长度--这意味着1个整数的值50只占用1个字节,而不是

解剖SQLSERVER 第十七篇 使用 OrcaMDF Corruptor 故意损坏数据库(译)

解剖SQLSERVER 第十七篇 使用 OrcaMDF Corruptor 故意损坏数据库(译) http://improve.dk/corrupting-databases-purpose-using-orcamdf-corruptor/ 有时候你必须先作恶,后行善.情况就是 当你想磨练你的数据库修复技能 我现在添加了一个Corruptor 类到OrcaMDF里面 去测试新的RawDatabase 的功能.Corruptor 就跟他的名字一样--他会故意损坏数据库文件 Corruptor 本身

解剖SQLSERVER 第十一篇 对SQLSERVER的多个版本进行自动化测试(译)

原文:解剖SQLSERVER 第十一篇 对SQLSERVER的多个版本进行自动化测试(译) 解剖SQLSERVER 第十一篇    对SQLSERVER的多个版本进行自动化测试(译) http://improve.dk/automated-testing-of-orcamdf-against-multiple-sql-server-versions/ 自从我发布了OrcaMDF Studio,我已经意识到SQL2005和SQL2008之间的一些系统表的差异. 这些差异导致OrcaMDF 解析失败

解剖SQLSERVER 第三篇 数据类型的实现(译)

解剖SQLSERVER 第三篇  数据类型的实现(译) http://improve.dk/implementing-data-types-in-orcamdf/ 实现对SQLSERVER数据类型的解析在OrcaMDF 软件里面是一件比较简单的事,只需要实现ISqlType 接口 public interface ISqlType { bool IsVariableLength { get; } short? FixedLength { get; } object GetValue(byte[]