在更新SQL Server上创建自定义ID(触发器?)

时间:2011-08-16 18:02:25

标签: .net sql sql-server-2008 linq-to-sql triggers

我有一个请求表,一旦批准/拒绝,他们需要分配一个自定义顺序ID,如:

MYCODE-11-0001
MYCODE-11-0002
MYCODE-11-0003 

其中MYCODE不会更改,但11是当前年份,而0001是按年份分配的顺序生成的编号。所以每年都会从0001开始。

我从未在SQL Sever中处理触发器或存储过程,因此我想通过VB.net中的Linq-to-SQL在代码中执行此操作,并且我在SQL Server中有另一个表来跟踪{ {1}}

Next ID

如果他们更改了网站上的状态,应用会根据当前年份选择名称并获取ID NAME NextID 1 Request2011 102 2 Request2012 1 3 Request2013 1 并生成NextID。 LINQ甚至可以捕获RequestID自抓取值后添加一个,并尝试保存该新值。

很确定这会有效,但不是我对此完全满意,因为它假设一切都来自我的应用程序。如果它在数据库级别上完成,我会感觉好多了,如果它不依赖于我的NextID表,那就更好了。

是否有任何触发器和存储过程不会太疯狂,以至于我无法生成这些自定义ID?我有一个模糊的想法,我可以在更新NextID时运行一个触发器来运行存储过程来执行一些重要工作(如果尚未设置则生成StatusID),但如果我仍然必须依赖RequestID表,然后我必须担心锁定/解锁表是否正确?

希望得到一些比我更了解SQL Server的人的建议。

注意:自定义ID仅在批准/拒绝请求时分配,而不是在插入数据库时​​分配。每个请求都有自己独特的自动生成身份PK。


更新:我可能不得不坚持使用代码解决方案,因为就并发性而言,它为我提供了所有的操作,但我确实编写了一个触发器,以便我可以学习。

NextID

这不做的是防止有人多次更改/* -------------------- SETUP --------------------*/ CREATE TABLE [dbo].[TestData]( [ID] [int] IDENTITY(1,1) NOT NULL, [data] [varchar](100) NULL, [RequestID] [varchar](25) NULL, [StatusID] [int] NULL ) INSERT INTO TestData ([data],[StatusID]) VALUES ('test',1) GO CREATE TABLE [dbo].[NextID]( [ID] [int] IDENTITY(1,1) NOT NULL, [Name] [varchar](75) NULL, [NextID] [int] NULL, [DateModified] [date] NULL, [ModifiedBy] [varchar](75) NULL ) INSERT INTO NextID ([Name],[NextID]) VALUES('RequestID2011',1) GO /* -------------------- TRIGGER --------------------*/ IF EXISTS (SELECT name FROM sys.objects WHERE name = 'UpdateStatusID' AND type = 'TR') DROP TRIGGER dbo.UpdateStatusID; GO CREATE TRIGGER UpdateStatusID ON dbo.TestData AFTER UPDATE AS IF ( UPDATE (StatusID)) BEGIN DECLARE @nextId int DECLARE @nextIdName varchar(50) /* -- Get NextID based on year's count IE: RequestID2011 */ SET @nextIdName = 'RequestID' + CONVERT(VARCHAR, YEAR(GetDate()), 50) SET @nextid = (Select NextID from dbo.NextID where Name = @nextIdName) /* -- Increment NextID */ UPDATE dbo.NextID set NextID = @nextid + 1 WHERE Name=@nextIdName /* -- Set New RequestID */ UPDATE dbo.TestData SET RequestID = 'MYCODE-' + RIGHT(@nextIdName,2) + '-' + CONVERT(VARCHAR, REPLICATE('0', (4- LEN(@nextid))) + @nextid, 50) FROM inserted i INNER JOIN dbo.TestData t ON i.id = t.id END; GO /* -------------------- TEST --------------------*/ UPDATE dbo.TestData SET StatusID = 3 WHERE ID = 1; GO (他们可以这样做,但我只需要生成一次ID)或防止人们抓住StatusID同时并且遇到竞争条件问题。

这仍然需要我在未来几年在数据库中有几个'RequestIDXXXX'记录。

警告:这不处理并发性,只是我理解触发器和SQL Server的过程中正在进行的工作。

4 个答案:

答案 0 :(得分:1)

是的,拥有这样一个下一个ID的表是一个热点,通常也是瓶颈 - 如果正确完成的话。如果没有,那么你将获得重复的ID ....

目前,SQL Server 2008 R2并没有真正为您提供良好的答案。这可能会随着SQL Server 2011(或可能是:SQL Server 2012)而发生变化,代号为“Denali”。 Denali将引入序列,它允许您在数据库核心的控制下创建顺序ID,并保证它们是唯一的。您还可以在每个新年的1月1日将序列重置为1。

查看Aaron Bertrand关于SQL Server vNext (Denali): Using SEQUENCE的博客文章,了解有关序列的更多信息。

现在,是的,我猜你的方法是使用Linq-to-SQL从客户端设置这些ID,或者在触发器内部,这可能是最合适的方法(我更喜欢触发器解决方案,我自己) 。我认为该表上的单个INSTEAD OF INSERT触发器就足够了 - 如果您插入一个新行,请获取您需要的新请求ID并将其设置为您刚刚插入的值。

答案 1 :(得分:1)

您确实可以使用桌面上的触发器执行此操作,请参阅下文:

create table NextID (
    id  int,
    name    varchar(10),
    nextid  int
)

create table TestData (
    sequence    varchar(14) NOT NULL,
    data    varchar(10)
)

insert into NextID
values(1, 'Req2011', 1)


--Important stuff starts here
ALTER TRIGGER MySequence
ON TestData
INSTEAD OF INSERT
AS
DECLARE @nextid int

SET @nextid = (select nextid from NextID ) - 1

UPDATE NextID
SET nextid = nextid + (select COUNT(*) from inserted)
WHERE id = 1;

WITH cte AS (
    select ROW_NUMBER() OVER (ORDER BY (select 1)) + @nextid as sequence, i.data
    from inserted i
)
insert into TestData
select 'MYCODE-' + RIGHT(YEAR(GETDATE()), 2) + '-' + RIGHT('000' + CAST(sequence as varchar), 4), data
from cte;

这是一种非常可靠的方法。请注意,写入触发器是为了允许一次插入多个记录。

参考: http://msdn.microsoft.com/en-us/library/ms189799.aspx

http://www.sqlmag.com/article/sql-server/nondeterministic-row-numbers

答案 2 :(得分:1)

您可以尝试这样的事情:

DECLARE @CustomID VARCHAR(255)
SELECT @CustomID = 'MYCODE-' + CAST((YEAR( GETDATE()) % 100) AS CHAR(2)) + '-' + REPLICATE('0', (4 - LEN((<Sequence> + 1)))) + CAST((<Sequence> + 1) AS VARCHAR(5))

修改

我不知道你是如何得到这个序列的,但是如果你想把它从桌子上的行数中拉出来,你可以这样做:

SELECT COUNT(SomethingID) --Optionally add +1
FROM   SomethingTable
WHERE  DATEDIFF(YEAR, SomeDateColumn, GETDATE()) = 0

答案 3 :(得分:0)

我认为这不是主要关键思路中的“id”。

您可以使用持久计算列。有什么影响:

ALTER TABLE myTable
ADD CustomID AS (

    'MYCODE-' + CAST(RIGHT(100 + year([date_field]),2) as varchar(2)) + CAST(RIGHT(10000 + [pk_field],4) as varchar(4))

) PERSISTED

这样,只要将新记录添加到表中,就可以完成工作。

这里要注意的是,如果您更新[pk_field]列(不太可能......?),您的计算列将重新计算。但除此之外,只要你使用'PERSISTED',它就不会改变。

修改
我将getdate()替换为[date_field]以上。我仍然认为计算列对你来说是一个不错的选择,但需要注意的是你需要记录上的日期字段(date_inserted,或者你有什么),因为getdate()(或其他非确定性值)不能使用。