项目中由于需求设计,数据库中需要一个timestamp时间戳类型的字段来作为区别数据添加和修改的标识。由于timestamp在SQL SERVER 2005数据库中,不可为空的timestamp类型在语义上等同于binary(8)类型,可为空的 timestamp类型在语义上等同于varbinary(8)类型,这将导致在C#程序中获取到的timestamp类型则变成了byte[]类型。所以如果我们需要从数据库中获取并使用这个时间戳的话就必需经过转换。
我们先建立一张测试表,语句如下:
CREATE TABLE [dbo].[tb_Ts](
[id] [int] IDENTITY(1,1) NOT NULL,
[TS] [timestamp] NULL,
[text] [nvarchar](50) NULL
) ON [PRIMARY]
表名为tb_Ts,只有三个字段id,TS和text。其中TS字段就是我们需要的时间戳字段
SQL Server 中timestamp类型的定义
首先看下timestamp在SQL Server 2005中的定义,该定义摘抄自SQL Server 2005联机丛书(具体详情点击此链接):
timestamp 公开数据库中自动生成的唯一二进制数字的数据类型。timestamp 通常用作给表行加版本戳的机制。 存储大小为 8 个字节。 timestamp 数据类型只是递增的数字,不保留日期或时间。 若要记录日期或时间,请使用 datetime 数据类型。
备注:
每个数据库都有一个计数器,当对数据库中包含 timestamp 列的表执行插入或更新操作时,该计数器值就会增加。 该计数器是数据库时间戳。 这可以跟踪数据库内的相对时间,而不是时钟相关联的实际时间。 一个表只能有一个 timestamp 列。 每次修改或插入包含 timestamp 列的行时,就会在 timestamp 列中插入增量数据库时间戳值。 这一属性使 timestamp 列不适合作为键使用,尤其是不能作为主键使用。 对行的任何更新都会更改 timestamp 值,从而更改键值。 如果该列属于主键,那么旧的键值将无效,进而引用该旧值的外键也将不再有效。 如果该表在动态游标中引用,则所有更新均会更改游标中行的位置。 如果该列属于索引键,则对数据行的所有更新还将导致索引更新。
使用某一行中的 timestamp 列可以很容易地确定该行中的任何值自上次读取以后是否发生了更改。 如果对行进行了更改,就会更新该时间戳值。 如果没有对行进行更改,则该时间戳值将与以前读取该行时的时间戳值一致。 若要返回数据库的当前时间戳值,请使用@@DBTS。
Transact-SQL timestamp 数据类型不同于在 SQL-2003 标准中定义的 timestamp 数据类型。 SQL-2003 timestamp 数据类型等同于 Transact-SQL datetime 数据类型。
rowversion 的数据类型为 timestamp 数据类型的同义词,并具有数据类型同义词的行为。 在 DDL 语句,请尽量使用 rowversion 而不是 timestamp。 有关详细信息,请参阅 数据类型同义词 (Transact-SQL)。
程序中获取出的timestamp
如何使用SQL语句插入timestamp字段值?
我们从上面的timestamp定义中知道了timestamp这个值一般都是数据库自动添加和修改的,相当于自动增长标识一样(而且执行update修改语句这个字段也会自动更新),所以一般这个字段我们只做查询操作。如果要更新这个字段则会提示这个错误信息:不能更新时间戳列。但是这个字段是可以手动添加的,不过也只能使用DEFALUT字段(default字段为SQL Service数据库的一个默认值),如果传入其他值则会提示错误信息:不能将显式值插入时间戳列。请对列列表使用 INSERT 来排除时间戳列,或将 DEFAULT 插入时间戳列。下面是添加timestamp的SQL语句:
INSERT INTO [tb_Ts]([TS]) VALUES(DEFAULT)
解决数据库中timestamp类型和C#中byte[]类型转换问题
在程序中我们发现,通过ADO.NET获取数据库中timestamp字段值到程序中,结果类型为byte[]。假设在数据库中timestamp的值为0x00000000000007D6,那么获取到.net程序中的值就不是这样了,一把来说会变成byte[]的数组类型。那么我们的解决方式有两种,第一种方式是直接在数据库中将timestamp进行转换,可以转换成十六进制字符串类型或者BIGINT的长整形,这也是我推荐的方法。还有一种是在.NET 程序中使用BitConverter方法进行转换。以下是两种方式的代码:
方法一(在SQL中转换):
SELECT TS
,CAST(TS AS VARBINARY(8)) AS ‘timestamp转十六进制字符串‘
,CONVERT(BIGINT,TS) AS ‘timestamp转bigint类型‘
FROM tb_Ts
这样一来我们就可以获取到timestamp的十六进制字符串或者bigint,最终查询出来的结果如下图:
另外要说明的一点是,VARBINARY(8)对应的c# 类型是byte[],所以建议直接转换成bigint类型,否则在C#中还要调用下面的方法
方法二(在程序中转换,调用下面的方法即可):
/// <summary>
/// 将数据库中timespan转换成十六进制字符串
/// </summary>
/// <param name="objTs">从数据库中获取的timespan值</param>
/// <returns>timespan十六进制字符串</returns>
public string ConvertToTimeSpanString(object objTs)
{
byte[] btTsArray=objTs as byte[];
string strTimeSpan = "0x"+ BitConverter.ToString(btTsArray).Replace("-","");
return strTimeSpan;
}