我需要生成具有以下格式的票证ID:
TicketType+YYMMDD+nnnn
我有一些已经工作了一年的东西,但今天发现了一个缺陷。
DECLARE @TktType varchar(4) = CASE @TypeId WHEN 1 THEN 'TKTT' WHEN 2 THEN 'TKTD' WHEN 3 THEN 'TKTV' WHEN 4 THEN 'TKTB' END
DECLARE @DatePart varchar(6) = CAST(YEAR(GetDate()) - 2000 AS varchar(4)) +
RIGHT('0' + CAST(MONTH(GetDate()) AS varchar(2)), 2) +
RIGHT('0' + CAST(DAY(GetDate()) AS varchar(2)), 2)
DECLARE @nextNum varchar(4) = (SELECT CONVERT(INT, MAX(SUBSTRING(SO, 11, 4))) + 1 FROM T_SO WHERE SO LIKE @TktType + @DatePart +'%')
SET @nextNum = RIGHT('000' + COALESCE(@nextNum, '1'), 4)
INSERT INTO tblTickets (TktID, ...)
VALUES (@TktType + @DatePart + @nextNum, ...)
这已经工作了一年顺利。你能猜出发生了什么吗?今天有两个人在同一时间点击它。两者都生成了相同的Ticket ID,并且由于TktID列是主键,因此其中一个得到了一个很好的"违反PRIMARY KEY约束"消息。
所以我考虑过为每种票证类型创建一个带有标识列和位列的新表。插入0并返回插入的id。这意味着必须截断表并每隔午夜重置身份种子。我确定这有不可预见的问题。
我还考虑过循环和递增数字,直到插入成功为止。坏。
我的一位同事建议使用一个事务来锁定表,这会让其他人等到我完成。不确定。
有没有其他人必须做类似的事情?我正在寻找有关如何最好地解决问题的建议和意见。
编辑:我想我有一些有用的东西。随意留下你的想法。首先,我创建了一个表,每个票证类型都有一行:
CREATE TABLE [dbo].[T_TicketID](
[id] [int] NOT NULL,
[TicketType] [varchar](4) NOT NULL PRIMARY KEY,
[Date] [date] NOT NULL
)
然后我创建了一个接受故障单类型并返回完整故障单ID的过程:
ALTER PROCEDURE usp_CreateTicketID
@TicketType varchar(4),
@TicketID varchar(14) OUTPUT
AS
SET NOCOUNT ON
DECLARE @Date DATE = GETDATE()
DECLARE @out TABLE (TicketID varchar(14))
UPDATE T_TicketID
SET
id = CASE WHEN [Date] = @Date THEN id + 1 ELSE 1 END,
[Date] = CASE WHEN [Date] = @Date THEN [Date] ELSE @Date END
OUTPUT @TicketType +
CONVERT(varchar, YEAR(@Date) - 2000) +
RIGHT('0' + CONVERT(varchar, MONTH(@Date)), 2) +
RIGHT('0' + CONVERT(varchar, DAY(@Date)), 2) +
RIGHT('000' + CONVERT(varchar, INSERTED.id), 4)
INTO @out
WHERE TicketType = @TicketType
SET @TicketID = (SELECT TicketID FROM @out)
由于UPDATE是原子的,它会对更新进行序列化,每个人都会获得一个唯一的TicketID。
我通过2个进程对它进行了测试,每个进程在一个循环中,每个进程10,000次,循环没有延迟。我将生成的TicketID保存到表中,然后验证没有重复项。
答案 0 :(得分:0)
如果您无法避免使用身份或GUID,则可以使用自定义序列来处理您遇到的竞争条件。 SQL Server将处理输出序列中下一个数字的困难部分,并使用cycle
参数将它包围并在最后一个数字minvalue
为maxvalue
时返回使用
create sequence dbo.TicketNumber as smallint
start with 1
increment by 1
minvalue 1
maxvalue 9999
cycle;
-- This will give you the next value in line each time it's run
select next value for dbo.TicketNumber as TicketNumber
我建议您不要将TicketType,date和递增数字存储在一个字段中。如果每个值都有一列,并且主键是所有3列,例如:
,那会更好create table dbo.tblTickets
(
TicketType char(4) not null,
TicketDate char(6) not null,
TicketNumber smallint not null,
constraint PK_TicketType_TicketDate_TicketNumber primary key
(
TicketType,
TicketDate,
TicketNumber
)
)
答案 1 :(得分:0)
除非您有一天要插入9999张以上的门票,否则在您开始输入值100年之后,您的工作将会正常工作,那么2位数的年份将为您提供。
事务包装是您需要实施的唯一更改,以防止您遇到的情况。
虽然其他人不同意,但以这种方式制作主键很好,它适合您的使用案例,您知道它的局限性,并且它在您的业务案例中比随机guid更有意义。
使用内置的guid或整数标识可以阻止您发生的错误,但如果您当前的type-date-number键具有任何业务价值,您将失去该错误,或者仍然必须维持该约束同时还要保持指导。