【问题标题】:Generate a unique time-based id on a table in SQL Server在 SQL Server 中的表上生成唯一的基于时间的 id
【发布时间】:2021-07-06 16:16:37
【问题描述】:

我正在尝试根据数据库服务器的时钟生成一个唯一的 ID/时间戳。这不是表的主要 ID,但我感兴趣的是,该值对于每一行都是唯一的。此代码在一个存储过程中,将由同时运行的各个进程同时执行。

我想出了这段代码,但找不到让它更优雅的方法。据我测试,确保其他进程不会在其他线程上生成相同值的唯一方法是在检查存在时锁定表。

DECLARE @CurrentRRN varchar(30)
SET @CurrentRRN = FORMAT(SYSDATETIME(), 'yyyyMMddHHmmssffffff')

IF(EXISTS(SELECT RRN FROM dbo.Numbering WITH (xlock, tablockx, holdlock) 
  WHERE RRN = @CurrentRRN)) BEGIN

  WHILE( EXISTS(SELECT RRN FROM dbo.Numbering WHERE RRN = @CurrentRRN) )
    BEGIN
        SET @CurrentRRN = FORMAT(SYSDATETIME(), 'yyyyMMddHHmmssffffff')
    END

END

INSERT INTO dbo.Numbering
(RRN)
Values
(@CurrentRRN)

print @CurrentRRN

【问题讨论】:

  • 应该是 datetime2 吗?也许你应该解释一下可能的目的是什么?
  • "...会同时执行..." 那样的话,怎么可能是唯一的呢?如果同时插入 2 条记录,则它不能是唯一的,除非您确实为整个过程锁定了表 - 这只会导致在最好的情况下缓慢插入,在最坏的情况下会导致死锁。如果你已经有一个主键,为什么还要让它唯一呢?
  • 为什么不使用唯一标识符 (NEWID())?这些将是独一无二的。如果您还想要一些日期时间(或日期时间2)值,只需添加一个额外的列。
  • 原因是客户是从日期(他们的措辞 :P )生成的“数字字符串 id”,除了另一个表上使用的唯一 id (int)。因此,“编号”表用于生成和跟踪此 ID。这就是为什么我在生成它之后检查它是否存在,如果它已经由并行进程创建,则生成一个新的。每秒不会有数千条记录,但多个进程将访问此过程,我想确保没有重复。
  • 首先考虑将RRN 更改为在“同一”时间插入的行的序列号,例如yyyyMMddHHmmssfffffff-nn。您可能需要考虑实现一个触发器,以便在为表执行 insert 时分配值。它将捕获SysDateTime,获取日期/时间的最大RRN 值(如果有),增加序列号并更新表。请注意,如果insert 涉及多行,则触发器将需要为行分配合适的、可能是顺序的值,可能按其他列排序。并处理溢出。

标签: sql sql-server tsql


【解决方案1】:

首先,阅读来自 cmets 的所有优秀建议和其他答案,了解为什么您可能应该做一些不同的事情。

但是,是的,您可以这样做,方法如下。基本上只需将 datetime2 的最后几位数字替换为序列中的值。您替换的数字越多,您可以在不违反唯一约束的情况下同时插入的行越多。在下面的示例中,我将用序列值替换 10 秒后的所有内容。如果您想保留更多亚秒精度的数字来存储尝试插入的实际时钟时间,您只需减少可以批量插入的最大行数。这取决于你。

这里是:

drop table if exists MyTableWithUniqueDatetime2

drop sequence if exists seq_ts_nanoseconds
go
create sequence seq_ts_nanoseconds
start with 0 
increment by 100
minvalue 0
maxvalue 99999900
cycle

go
create or alter function GetTimestamp(@ts datetime2, @ns int)
returns datetime2(7)
as
begin
 return  dateadd(ns,@ns, dateadd(ns,-(datepart(ns,@ts) % 100000),@ts))
end
go
create table MyTableWithUniqueDatetime2
(
  id bigint identity,
  a int,
  b int,
  ts datetime2(7) default dbo.GetTimestamp(sysdatetime(), (next value for seq_ts_nanoseconds)) unique

)
go
select sysdatetime()
insert into MyTableWithUniqueDatetime2 (a,b)
output inserted.*
select top (1000) 1,2
from sys.objects o, sys.columns c, sys.columns c2

【讨论】:

  • 好吧,这将避免担心锁定,但也会将纳秒精度抛到窗外,但另一方面,我的意思是,假设您要插入 300,000 行 - 第一个将获得准确的时间,但是当您将第 300,000 行插入数据库时​​,您的序列现在返回 3,000,000 - 这是一百万纳秒的时间。不确定它有那么重要,但话又说回来,基于插入行的时间的唯一索引首先是一个坏主意。
  • 是的。但请注意,普通计算机以毫秒范围内的时间片共享 CPU 时间。因此,测量计算机何时执行某项操作只能精确到小数点后 3 或 4 位。如果你存储的是一些超精确的外部设备生成的数据,datetime2(7) 的 100ns 分辨率可能有意义,但对于使用 SYSDATETIME() 生成的数据,它就没有意义了。
  • 这是我不知道的难题之一...感谢您提供的信息!
  • 我采用了这个解决方案,提供了生成唯一 ID 并保留时间戳的最简单方法!我不知道你可以像这样使用内置序列,所以我学到了很多!谢谢!!!
  • 我也不知道,直到你问这个问题:)
【解决方案2】:

不要这样做。创建一个数字 identity 列。在大多数情况下,这将跟踪插入的顺序(这在多线程环境中有点棘手,但应该足够接近)。

添加另一列,例如createdAt,默认值为时间戳。您可以将其设为datetime2 列以获得更高的精度。

如果你真的需要一个唯一的数值,我建议创建一个类型类似于decimal(38, 10) 的计算列。它的结构是这样的:YYYYMMDDHHMMSSFFFFFF..(“F”是小数秒。)

【讨论】:

  • 时间戳,无论你走多长时间或小数,仍然可以获得两个精确值。我最终走的是接受答案的路线,即将时间戳与序列相结合(类似于身份列)
  • @DaniDaniDr 。 . .如果您只想要一个唯一的列,请使用 identity 并将其声明为 unique
【解决方案3】:

hi 尝试了一个简单的性能测试,使用 Sequence 而不是 Identiy。

有 1000000 行,标识需要 20 秒,序列 >50 秒。

这是我的测试:

drop sequence if exists seq_ts_nanoseconds
go
create sequence seq_ts_nanoseconds as int
start with 0 
increment by 1
minvalue 0
maxvalue 1000000
cycle


--drop table #t1
create table #t1
(
    id int identity(1,1),
    v nvarchar(50)
)

;with
tmp1 as
(
    select 0 c union all
    select t1.c+1
    from tmp1 t1
    where t1.c+1<1000000
)
insert #t1(v)
select t1.c
from tmp1 t1
option (maxrecursion 0)

------------------------

--drop table #t2
create table #t2
(
    id int,--identity(1,1),
    v nvarchar(50)
)

;with
tmp1 as
(
    select 0 c union all
    select t1.c+1
    from tmp1 t1
    where t1.c+1<1000000
)
insert #t2(id,v)
select next value for seq_ts_nanoseconds,
       --ROW_NUMBER()OVER(ORDER BY(SELECT 0)),
       t1.c
from tmp1 t1
option (maxrecursion 0)

这里有一个在线示例:https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=34180889596d24d1237c1723f3746f71

【讨论】:

    猜你喜欢
    • 2013-10-02
    • 1970-01-01
    • 1970-01-01
    • 2015-01-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多