Mat*_*hew 4 sql-server primary-key sql-server-2014
我有一个连续修改的已处理事件表。处理时间的 UTC 时间和 eventId。
使用 SYSUTCDATETIME() 作为主键是否安全?我可以轻松使用代理键,但感觉很有趣。
Sol*_*zky 11
(请注意,问题的原始措辞是:“SYSUTCDATETIME 是安全的聚集索引吗?”)
你所说的“安全”究竟是什么意思?聚集索引不需要是唯一的,所以它是“安全的”,不会破坏任何东西。但它不能是主键,因为不能保证它是唯一的。只需尝试以下操作,您将看到从函数返回的值在所有行中都是相同的:
SELECT SYSUTCDATETIME(), * FROM sys.objects;
Run Code Online (Sandbox Code Playgroud)
那么问题来了:使用SYSUTCDATETIME()
代替 ,您究竟会获得什么IDENTITY
?我假设您使用的DATETIME2
是 8 个字节的列,但您很可能会使用 4 个字节INT
的IDENTITY
列。
所以,我会选择一个INT
填充的通孔,IDENTITY
因为它是:
更新
不,我个人不会相信一个值,即使是像 那样的高分辨率DATETIME2
,因为它仍然有可能在同一微秒内发生两个操作。并且,您不能保证将来任何时候都不会有人或某个进程在单个语句中插入超过 1 条记录。
更新 2
@ypercube??在下面的评论中提出了一个很好的观点(现在我在这里提到它的评论很快就会神奇地消失;-):
您可以将 datetime 列用于聚集索引,将标识列用于 PK。(这有时很好,如果您的所有或几乎所有查询都依赖于按日期时间排序。所以偶尔不匹配两个订单不会影响性能。)
是的,这有时是一个不错的选择,但由于聚集索引键被复制到非聚集索引中,它并非没有潜在的后果:
如果该表上只有一个非聚集索引,则索引占用的空间将大体相同。但是因为聚集索引键最终必须是唯一的(至少在内部),因为它们是非聚集索引的 RowID,对于具有重复DATETIME2
值的所有行,将添加额外的 4 字节“唯一标识符”。它将被添加两次:一次添加到聚集索引,一次添加到 IDENTITY PK 的非聚集索引。
如果有多个非聚集索引,那么对使用空间的影响将是:
(最少 4 个字节 + 任何重复的聚集索引键值 4 个字节)
* number_of_indexes
* rows_in_table(如果使用过滤索引则更少)
这些数字对某些人来说可能看起来很小,但如果我们处理数亿行,它们确实会加起来。对于那些错误地认为“磁盘便宜”的人,请考虑 a)企业存储不便宜,b)索引维护等磁盘操作也不便宜(SSD 问题不大,但仍然如此)。有关数据建模决策的下游影响的更详细分析,请参阅我在 SQL Server Central 上发表的以下文章:磁盘便宜!奥利?(该网站需要免费注册才能查看内容)。
这并不是说“不要做”,而是“只有在收益大于成本的情况下才做”。
更新 3
为了完整起见,我应该提到使用DATETIME2
值以希望分离个体INSERT
和/或UPDATE
语句的一个问题是它们不像看起来那么细粒度。这是“分辨率”和“精度”之间的问题。的“精度”DATETIME2
是 7 个小数位。但这并不意味着所表示的最细粒度的时间值会在下一个值发生时递增。这就像DATETIME
精确到毫秒的值一样,你永远不会得到一个在毫秒位置只有 0、3 或 7 的值:
SELECT CONVERT(DATETIME, '2016-04-14 20:30:40.121'), -- 2016-04-14 20:30:40.120
CONVERT(DATETIME, '2016-04-14 20:30:40.122'), -- 2016-04-14 20:30:40.123
CONVERT(DATETIME, '2016-04-14 20:30:40.128'); -- 2016-04-14 20:30:40.127
Run Code Online (Sandbox Code Playgroud)
同样,对于DATETIME2
值,有时调用 toSYSUTCDATETIME()
是完全准确的。但是因为该值不会在下一微秒刷新,所以报告的值将保持不变,直到下一次刷新。这就是为什么@Paul 在关于问题的评论中发布的简单测试会违反 PK,即使这两个INSERT
语句可以说相隔至少 1 微秒。
有关更多详细信息,请参阅以下 Stack Overflow 答案,包括指向 CodeProject.com 上的项目的链接,该项目具有可在 SQLCLR 中使用以克服此限制的代码:
归档时间: |
|
查看次数: |
776 次 |
最近记录: |