我有一个请求表,一旦批准/拒绝,他们需要分配一个自定义顺序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的过程中正在进行的工作。
答案 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()
(或其他非确定性值)不能使用。