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'