Sql server 具有不同值的datetime上的主键冲突

Sql server 具有不同值的datetime上的主键冲突,sql-server,tsql,datetime,primary-key,Sql Server,Tsql,Datetime,Primary Key,让我们忽略一个事实,datetime被用作主键 鉴于以下情况: DECLARE @table table (test datetime primary key) INSERT INTO @table ( test ) select '2015-09-21 00:00:00.001' -- test - datetime -- successful insert INSERT INTO @table ( test ) select '2015-09-21 00

让我们忽略一个事实,
datetime
被用作主键

鉴于以下情况:

DECLARE @table table (test datetime primary key)

INSERT INTO @table
(
    test
)
select
    '2015-09-21 00:00:00.001' -- test - datetime
-- successful insert 

INSERT INTO @table
(
    test
)
select
    '2015-09-21 00:00:00.002' -- test - datetime
-- successful insert

INSERT INTO @table
(
    test
)
select
    '2015-09-21 00:00:00.003' -- test - datetime
-- fails due to primary key violation

select * from @table


这是怎么回事?所有三个“尝试”插入的值都是不同的,但是
.002
.003
被认为是“相同的值”

日期时间数据类型没有那么高的精度。尝试此查询以查看这些值会发生什么情况。datetime数据类型仅精确到.003


看起来它没有以不同的方式考虑毫秒部分,但是如果使用
DATETIME2
作为数据类型,那么它将成功,因为
DATETIME2
将产生更好的精度。请尝试以下内容:

DECLARE @table table (test datetime2 primary key)

INSERT INTO @table
(
    test
)
select
    '2015-09-21 00:00:00.001' -- test - datetime
-- successful insert 

INSERT INTO @table
(
    test
)
select
    '2015-09-21 00:00:00.002' -- test - datetime
-- successful insert

INSERT INTO @table
(
    test
)
select
    '2015-09-21 00:00:00.003'  
日期时间值四舍五入为.000、.003或.007的增量 秒,如下表所示

源MSDN

因此,您的表值
2015-09-21 00:00:00.002
被四舍五入为
2015-09-21 00:00:00.003
,并且在上次插入时,您得到主键冲突

Datetime可能是主键上最糟糕的数据类型之一。在表中查找替代列或添加代理列作为主键列

这些不是唯一的

准确度 四舍五入到.000、.003或.007秒的增量

可能不使用datetime作为PK


正如肖恩在回答中指出的,这是一个精度问题。使用
DATETIME2(3)
代替(SQL Server 2008和更新版本),这些问题就会消失,因为它们以“输入”的精度存储,但是通过四舍五入到最接近的.003毫秒来比较相等性?不,存在隐式转换,并且值被四舍五入到最接近的.003毫秒。虽然
datetime2
确实具有更好的精度,但它仍然可能是类似行为的受害者。默认情况下,
datetime2
的精度为7个小数位数,即100ns,但用户可以指定精度在0到7位之间。因此,
datetime2
仍然可以进行舍入,这取决于表列的精度定义方式和插入值的精度。
DECLARE @table table (test datetime2 primary key)

INSERT INTO @table
(
    test
)
select
    '2015-09-21 00:00:00.001' -- test - datetime
-- successful insert 

INSERT INTO @table
(
    test
)
select
    '2015-09-21 00:00:00.002' -- test - datetime
-- successful insert

INSERT INTO @table
(
    test
)
select
    '2015-09-21 00:00:00.003'