【问题标题】:Creating Custom ID on update SQL Server (trigger?)在更新 SQL Server 时创建自定义 ID(触发器?)
【发布时间】:2011-08-16 18:02:25
【问题描述】:

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

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

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

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

ID     NAME          NextID
1      Request2011    102
2      Request2012    1
3      Request2013    1

如果他们更改了网站上的状态,应用程序会根据当前年份选择名称并获取NextID 并生成RequestID。如果NextID 在获取值、加一并尝试保存该新值后发生了变化,LINQ 甚至会捕捉到。

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

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

只是希望从比我更了解 SQL Server 的人那里得到一些建议。

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


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

/* --------------------
        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

这不能防止有人多次更改 StatusID(他们可以这样做,但我只需要生成一次 ID)或防止有人同时抓取 NextID 并拥有比赛条件问题。

这还需要我在数据库中有几条“RequestIDXXXX”的记录,以备将来使用。

警告:这不处理并发,只是我理解触发器和 SQL Server 的过程中的一项工作。

【问题讨论】:

  • 您是否也在表中存储了日期时间值?每年不超过 9999 行?
  • 我在 RequestID 的同时设置了一个审核日期。永远不要说永远,但每年的请求不应超过 2000 个。
  • 您的触发器仅适用于插入的一条记录。如果您一次插入多条记录,它们都会以相同的序列 ID 结束。此外,您应该更新您的数据表之前增加您的计数器。
  • 如果发生两次更新,这不会导致两个触发器运行并且它们都将 ID 与插入的表匹配吗?更新了增量位置,但这个触发器没有很多其他问题,这就是为什么我不使用它并且确实提到这根本不处理并发。也许这可以帮助一些人在未来如何开始。

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


【解决方案1】:

是的,为下一个 ID 提供这样的表是一个热点,而且通常也是一个瓶颈 - 如果操作正确的话。如果没有,那么您将获得重复的 ID....

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

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

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

【讨论】:

  • 目前看来 Denali 可能是 SQL Server 2012。
  • 哇,看到我真正需要的东西却不能使用它,这很痛苦。谢谢你分享它。顺便说一句,自定义 ID 是基于请求被批准/拒绝的时间而不是当它被插入数据库时​​,但很高兴知道类似 INSTEAD OF INSERT 的东西也存在。
  • 您绝对可以修改我上面的示例,根据已批准的拒绝使用不同的 ID。您可以在 ROW_NUMBER() 函数中使用 PARTITION BY 来获得所需的内容。
【解决方案2】:

你确实可以用你的桌子上的触发器来做到这一点,见下文:

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

【讨论】:

  • @James - 该评论绝对不正确。它将返回insert 语句插入的行数。
  • @Martin:在插入结果集的情况下,对吗?然而,在他的插入语句的情况下,插入表将只包含一行。无论如何,我想说的是它不能用于计算序列。
  • 可以。该变量用作基础,然后添加到 row_number 函数的结果,从而生成一个序列。不信就试试看。
【解决方案3】:

你可以试试这样的:

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

【讨论】:

  • 不是我需要的,因为我特别要求的是如何生成序列 ID,而不是如何根据选定的序列 ID 创建字符串。但是 +1 因为我确实需要 REPLICATE
【解决方案4】:

我建议这不是主键思路中的“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',它将保持不变。

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

【讨论】:

  • 但是 GETDATE() 不是常数,所以值每年都会变化。它应该基于另一列。这假设计算列中允许使用 GETDATE
  • @gbn -- 好的 -- 你是对的。我刚刚测试过,如果您尝试使用非 det 值,它甚至不会创建计算列。 (错误 - 表 'tbl_cete' 中的计算列 'custom' 无法持久化,因为该列是不确定的)。我会修改我的答案。
  • 当数据第一次对我用于我的应用程序的数据库感兴趣时,我有一个唯一的身份 int PK。此自定义 ID 用于报告系统,格式和按年编号的要求超出了我的控制范围。在请求被正式批准/拒绝之前,我不能给它“数字”部分。一些预筛选比其他预筛选需要更长的时间。如果我不必每年都重置数字,我只需将 MYCODE-REVIEWYEAR-PKID 添加到视图列并完成它。
猜你喜欢
  • 2014-02-02
  • 2020-01-07
  • 2016-11-03
  • 2011-06-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-22
  • 1970-01-01
相关资源
最近更新 更多