我有一个使用ADODB与sqloledb提供程序连接到SQL Server 2012的经典ASP站点。当用户点击链接时,我将一条记录插入表中以跟踪点击。该表的主键由三列组成:id [int],clickTime [datetime2(7)]和remoteAddress [varchar(15)]。我的insert语句提供id和remoteAddress值,clickTime列设置为默认值SYSDATETIME()。一个例子是:
INSERT MyClick(id,remoteAddress)VALUES(766319,'108.178.236.50')
我每天都会获得几千次点击,但是我每天都会收到一些错误的重复密钥。这是一个例子:
违反PRIMARY KEY约束'PK_MyClick_1'。无法插入 对象'dbo.MyClick'中的重复键。重复的键值是 (766319,2016-01-20 15:30:14.1570772,108.178.236.50)。
由于流量相对较小,用户的IP地址包含在密钥中,以及datetime2的极高精度,我无法理解我是如何偶尔获得重复密钥的。我知道我可以在密钥中添加一个标识或GUID列以保证唯一性。我也明白,我可以在失败的情况下重试插入。但是,我真的很想理解这一点:
如何在我的方案中获得重复的密钥?
编辑:我弄清楚为什么网站试图插入两个类似的记录(对于相同的id和remoteAddress)。它起源于几年前一个错误的尝试,以解决弹出窗口阻止者破坏我们网站的问题。该网页的<a>
标记围绕着<div>
标记。 <a>
标记用_blank目标打开页面,<div>
有一个onClick事件,它使用window.open在新窗口中打开同一页面。此onClick通常会被弹出窗口阻止程序阻止,因此只会按预期打开一个页面。但是,如果用户允许该站点的弹出窗口(或使用没有弹出窗口阻止的浏览器),它实际上会在两个不同的选项卡中打开同一页面。这两个页面都试图插入数据库记录,如果它们足够接近,则会发生重复键错误。我在数据库中发现的是,我们有很多近似重复记录的情况,分开的时间只有几分之一秒。因此,如果我修复网站不在两个不同的窗口中打开页面,我相信这个重复的问题将消失(即使它仍然在技术上可行)。感谢那些提出建议帮助我解决问题的人。
答案 0 :(得分:2)
这里有两个问题。
首先,即使具有datetime2数据类型的精度,SQL Server也可以插入具有相同值的多个记录。在一个循环中插入100,000条记录并将datetime2列设置为sysdatetime(),我只获得了1,285个唯一值。在datetime2列中有多达128个具有相同值的记录,因此我的数据库服务器如此强大,它可以极其高效地插入记录,或者sysdatetime()函数的准确性不是人们所期望的。无论哪种方式,您都不能依赖sysdatetime()获取唯一值。
然而,在我的密钥中包含两个其他列(包括用户的IP地址),我仍然可以获得重复的密钥,这似乎很奇怪。这表明两个具有相同IP地址的人恰好在同一时间查看完全相同的数据(千种可能性)。事实证明,对于没有阻止弹出窗口的浏览器,请求的网页同时在两个不同的选项卡中打开。这通常会在数据库中生成一对记录,其datetime2值略有不同,但偶尔会在两个页面加载足够接近时生成重复键。我更正了网页,重复项已完全停止。
感谢@JuanCarlosOropeza帮助他思考这个问题。