Microsoft SQL Server:每天生成一个序列号
我的任务是每天为项目创建一个不断增加的序列号。多个进程(理论上在多台机器上)需要生成它。它最终就像
[date]_[number]
因为
20101215_00000001
20101215_00000002
...
20101216_00000001
20101216_00000002
...
我在这个项目中使用 SQL Server (2008) 无论如何,我尝试用 T-SQL/SQL 魔法来做到这一点。这就是我现在所处的位置:
我创建了一个包含序列号的表,如下所示:
CREATE TABLE [dbo].[SequenceTable](
[SequenceId] [bigint] IDENTITY(1,1) NOT NULL,
[SequenceDate] [date] NOT NULL,
[SequenceNumber] [int] NULL
) ON [PRIMARY]
到目前为止,我的天真的解决方案是一个触发器,在插入后设置 SequenceNumber:
CREATE TRIGGER [dbo].[GenerateMessageId]
ON [dbo].[SequenceTable]
AFTER INSERT
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- The ID of the record we just inserted
DECLARE @InsertedId bigint;
SET @InsertedId = (SELECT SequenceId FROM Inserted)
-- The next SequenceNumber that we're adding to the new record
DECLARE @SequenceNumber int;
SET @SequenceNumber = (
SELECT SequenceNumber FROM
(
SELECT SequenceId, ROW_NUMBER() OVER(PARTITION BY SequenceDate ORDER BY SequenceDate ASC) AS SequenceNumber
FROM SequenceTable
) tmp
WHERE SequenceId = @InsertedId
)
-- Update the record and set the SequenceNumber
UPDATE
SequenceTable
SET
SequenceTable.SequenceNumber = ''+@SequenceNumber
FROM
SequenceTable
INNER JOIN
inserted ON SequenceTable.SequenceId = inserted.SequenceId
END
正如我所说,这相当天真,并且保留了一整天的时间行只是用于我永远不再需要的单个数字:我执行插入,获取生成的序列号,然后忽略该表。不需要将它们存储在我这边,我只需要生成它们一次。此外,我很确定这不会很好地扩展,表包含的行越多,速度就会逐渐变慢(即我不想陷入“在我的开发机器上仅运行 10.000 行”的陷阱)。
我想目前的方式更多的是我用一些创造力来看待 SQL,但结果似乎 - 呃 - 不太有用。更聪明的想法?
I'm tasked to create an increasing sequence number per day for a project. Multiple processes (theoretically on multiple machines) need to generate this. It ends up as
[date]_[number]
like
20101215_00000001
20101215_00000002
...
20101216_00000001
20101216_00000002
...
Since I'm using an SQL Server (2008) in this project anyway, I tried to do this with T-SQL/SQL magic. This is where I am right now:
I created a table containing the sequence number like this:
CREATE TABLE [dbo].[SequenceTable](
[SequenceId] [bigint] IDENTITY(1,1) NOT NULL,
[SequenceDate] [date] NOT NULL,
[SequenceNumber] [int] NULL
) ON [PRIMARY]
My naive solution so far is a trigger, after insert, that sets the SequenceNumber:
CREATE TRIGGER [dbo].[GenerateMessageId]
ON [dbo].[SequenceTable]
AFTER INSERT
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- The ID of the record we just inserted
DECLARE @InsertedId bigint;
SET @InsertedId = (SELECT SequenceId FROM Inserted)
-- The next SequenceNumber that we're adding to the new record
DECLARE @SequenceNumber int;
SET @SequenceNumber = (
SELECT SequenceNumber FROM
(
SELECT SequenceId, ROW_NUMBER() OVER(PARTITION BY SequenceDate ORDER BY SequenceDate ASC) AS SequenceNumber
FROM SequenceTable
) tmp
WHERE SequenceId = @InsertedId
)
-- Update the record and set the SequenceNumber
UPDATE
SequenceTable
SET
SequenceTable.SequenceNumber = ''+@SequenceNumber
FROM
SequenceTable
INNER JOIN
inserted ON SequenceTable.SequenceId = inserted.SequenceId
END
As I said, that's rather naive, and keeps a full day of rows just for a single number that I never need again anyway: I do an insert, get the generated sequence number and ignore the table afterwards. No need to store them on my side, I just need to generate them once. In addition I'm pretty sure this isn't going to scale well, gradually getting slower the more rows the table contains (i.e. I don't want to fall into that "worked on my dev machine with 10.000 rows only" trap).
I guess the current way was more me looking at SQL with some creativity, but the result seems to be - erm - less useful. More clever ideas?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
忘掉那个
SequenceTable
吧。您应该只在最终表上创建两列:日期时间和身份。如果您确实需要将它们组合起来,只需添加一个计算列即可。我想它会是这样的:
这种方式将会扩展——你不会创建任何类型的中间或临时数据。
编辑 我仍然认为上面的答案是最好的解决方案。但还有另一种选择:计算列可以引用函数...
所以这样做:
它很难看,但是有效,对吧? :-) 没有任何临时表,没有视图,没有触发器,并且它将具有不错的性能(当然,至少有一个超过
SequenceId
和SequenceDate
的索引)。您可以删除记录(因为结果计算字段使用了身份)。Forget about that
SequenceTable
. You should just create two columns on your final table: a datetime and a identity. And if you really need them to be combined, just add a computed column.I guess it would be something like that:
That way will scale - you are not creating any kind of intermediary or temporary data.
Edit I still think that the answer above is the best solution. BUT there is another option: computed columns can reference functions...
So do this:
It's ugly, but works, right? :-) No temporary table whatsoever, no views, no triggers, and it will have a decent performance (with at least an index over
SequenceId
andSequenceDate
, of course). And you can remove records (since and identity is being used for the resulting computed field).如果您可以使用不同的名称创建实际的表,并通过视图执行所有其他操作,那么它可能符合要求。它还要求不删除任何事务(因此您需要在视图/表上添加适当的触发器/权限以防止这种情况发生):
结果:
当然,您也可以从视图中隐藏日期列并进行默认为 CURRENT_TIMESTAMP。
If you can create the actual table with a different name, and perform all of your other operations through a view, then it might fit the bill. It does also require that no transaction is ever deleted (so you'd need to add appropriate trigger/permission on the view/table to prevent that):
Result:
You can, of course, also hide the date column from the view and make it default to CURRENT_TIMESTAMP.
您可以执行类似的操作,
如果您在 SequenceDate 和 SequenceId 上创建索引,我认为性能不会太差。
编辑:
上面的代码可能会丢失一些序列号,例如,如果事务插入一行然后回滚(身份值将在空间中丢失)。
这个问题可以用这个视图来解决,它的性能可能不够好,也可能不够好。
我的猜测是,在您开始每天获得数百万个序列号之前,这已经足够了。
You could do something like
If you create an index on the SequenceDate and SequenceId, I don't think the performance will be too bad.
Edit:
The code above might miss some sequence numbers, for example if a transaction inserts a row and then rolls back (the identity value will then be lost in space).
This can be fixed with this view, whose performance might or might not be good enough.
My guess is that it will be good enough until you start getting millions of sequence numbers per day.
我尝试用这种方法来创建用于用户日志记录及其工作的会话代码;
生成的代码是:
'20170822-001'
,'20170822-002'
,'20170822-003'
I tried this way to create session codes for user logging and its working;
generated codes are:
'20170822-001'
,'20170822-002'
,'20170822-003'
如果您不介意数字不从 1 开始,您可以使用
DATEDIFF(dd, 0, GETDATE())
这是自 1-1-1900 以来的天数。每天都会增加。If you don't mind the numbers not starting at one you could use
DATEDIFF(dd, 0, GETDATE())
which is the number of days since 1-1-1900. That will increment every day.