Microsoft SQL Server:每天生成一个序列号

Ben*_*zun 5 sql t-sql sql-server sequence

我的任务是每天为项目创建一个不断增加的序列号。多个进程(理论上在多台机器上)需要生成它。最终结果是

[date]_[number]
Run Code Online (Sandbox Code Playgroud)

喜欢

20101215_00000001
20101215_00000002
...
20101216_00000001
20101216_00000002
...
Run Code Online (Sandbox Code Playgroud)

由于我在这个项目中使用的是 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]
Run Code Online (Sandbox Code Playgroud)

到目前为止,我的天真的解决方案是在插入后设置一个触发器,设置 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
Run Code Online (Sandbox Code Playgroud)

正如我所说,这是相当天真的,并且只为一个我永远不再需要的数字保留一整天的行:我进行插入,获取生成的序列号,然后忽略该表。不需要将它们存储在我这边,我只需要生成它们一次。此外,我很确定这不会很好地扩展,表包含的行越多,速度就会逐渐变慢(即我不想陷入“在我的开发机器上仅运行 10.000 行”的陷阱)。

我想目前的方式更多的是我用一些创造力来看待 SQL,但结果似乎 - 呃 - 不太有用。更聪明的想法?

rse*_*nna 5

把它给忘了SequenceTable。您应该只在最终表上创建两列:日期时间和身份。如果您确实需要将它们组合起来,只需添加一个计算列即可。

我想它会是这样的:

CREATE TABLE [dbo].[SomeTable] (
    [SequenceId] [bigint] IDENTITY(1,1) NOT NULL,
    [SequenceDate] [date] NOT NULL,
    [SequenceNumber] AS (CAST(SequenceDate AS VARCHAR(10)) + '_' + RIGHT('0000000000' + CAST(SequenceID AS VARCHAR(10)), 10)) PERSISTED
) ON [PRIMARY]
Run Code Online (Sandbox Code Playgroud)

这种方式将会扩展——您不会创建任何类型的中间或临时数据。

编辑我仍然认为上面的答案是最好的解决方案。但还有另一种选择:计算列可以引用函数......

所以这样做:

CREATE FUNCTION dbo.GetNextSequence (
    @sequenceDate DATE,
    @sequenceId BIGINT
) RETURNS VARCHAR(17)
AS
BEGIN
    DECLARE @date VARCHAR(8)
    SET @date = CONVERT(VARCHAR, @sequenceDate, 112)

    DECLARE @number BIGINT
    SELECT
        @number = COALESCE(MAX(aux.SequenceId) - MIN(aux.SequenceId) + 2, 1)
    FROM
        SomeTable aux
    WHERE
        aux.SequenceDate = @sequenceDate
        AND aux.SequenceId < @sequenceId

    DECLARE @result VARCHAR(17)
    SET @result = @date + '_' + RIGHT('00000000' + CAST(@number AS VARCHAR(8)), 8)
    RETURN @result
END
GO

CREATE TABLE [dbo].[SomeTable] (
    [SequenceId] [bigint] IDENTITY(1,1) NOT NULL,
    [SequenceDate] [date] NOT NULL,
    [SequenceNumber] AS (dbo.GetNextSequence(SequenceDate, SequenceId))
) ON [PRIMARY]
GO

INSERT INTO SomeTable(SequenceDate) values ('2010-12-14')
INSERT INTO SomeTable(SequenceDate) values ('2010-12-15')
INSERT INTO SomeTable(SequenceDate) values ('2010-12-15')
INSERT INTO SomeTable(SequenceDate) values ('2010-12-15')
GO

SELECT * FROM SomeTable
GO

SequenceId           SequenceDate SequenceNumber
-------------------- ------------ -----------------
1                    2010-12-14   20101214_00000001
2                    2010-12-15   20101215_00000001
3                    2010-12-15   20101215_00000002
4                    2010-12-15   20101215_00000003

(4 row(s) affected)
Run Code Online (Sandbox Code Playgroud)

虽然很丑,但是很管用,对吧?:-) 没有任何临时表,没有视图,没有触发器,并且它将具有不错的性能(当然至少有一个超过和 的索引)SequenceIdSequenceDate可以删除记录(因为结果计算字段使用了身份)。

  • 不 - 不幸的是,这没有帮助:这错过了“在新的一天重新启动”边界。我每天需要一个新序列,从 0 或 1 开始。 (3认同)