【问题标题】:Auto-generating value for a field based on the value of primary key, current year and previous year根据主键、当年和上年的值自动生成字段值
【发布时间】:2020-03-30 06:44:16
【问题描述】:

我有一个非常有趣的问题。我正在尝试根据“ID”(主键字段)的值生成“名称”字段的值。

例如,

'ID' 是 142

“名称”将为 19142。“19”表示当前年份。

为此,我编写了以下触发器(效果很好):

CREATE TRIGGER [dbo].[GenerateTheName]
ON [dbo].[Table1]
AFTER INSERT
AS
BEGIN

UPDATE [dbo].[Table1]

SET [Name] = (SELECT FORMAT(GETDATE(),'yy')) +
             (SELECT CAST(FORMAT([ID],'000','en-US') AS VARCHAR(4)) FROM inserted)

WHERE ID = (Select [id] FROM inserted)

END

现在,当年份发生变化时,'Name' 字段值中的 id 部分需要从零重新开始,而实际 ID 应遵循标识并像往常一样加 1。

所以如果 2019 年的最后一条记录 ID=164,那么 2020 年的第一条记录应该是:

ID:165 姓名:20001

实现此目的的一种方法是创建一个新表“table2”并在其中存储(通过触发器)当前年份(2019 年)的“ID”的最后一个值。那么2020年整个明年,这个记录的数据都可以作为参考。

所以,如果 2020 年的第一条记录的 ID 为:165 在“table1”中插入新记录后,另一个触发器将从“ID”的值中减去 table2 中的唯一值 (164)。

165-164=1
166-164=2
167-164=3
....

当 2020 年也结束时,“table2”中的这条唯一记录将被删除,并再次将 2020 年的最后一个 id 存储在其中。

这是解决问题的正确方法吗?请建议我解决它的可能方法。

提前致谢。

【问题讨论】:

  • 您正在使用inserted 伪表,就好像只包含一条记录一样。但事实并非如此。如果多行受到影响,inserted 也将包含尽可能多的行,您的触发器将失败。
  • 我将它与在线门户一起使用,每条记录都通过 GUI 插入。没有批量插入记录。
  • 在 DBMS 之外做什么并不重要。 DBMS 能够一次插入多条记录。因此触发器坏了。也许有一天你会在你的外部程序中进行更改或将另一个程序附加到数据库中,这允许一次生成多个记录并且你突然遇到了错误。永远不要说“客户端过滤它,所以可以忽略服务器端的问题。”。
  • 好的,感谢您指出这个问题。您能否建议一种方法来解决这种情况,而不是使用插入的?
  • 这不是问题,你使用inserted,是你如何使用它的问题。这是一套,所以你必须像对待一套一样对待它。而不是有一个UPDATEWHERE ID = (Select [id] FROM inserted),当inserted 中有不止一行时会中断,您需要做一个连接insertedUPDATE,以便它也适用于其中的多行。

标签: sql sql-server database triggers auto-generate


【解决方案1】:

如果您愿意在记录插入记录时的当前日期时间的表中添加另一列,则可以使用 1 个表和 1 个触发器来完成。示例代码 -

CREATE TABLE Table1 (ID INT IDENTITY(1,1) PRIMARY KEY, Name NVARCHAR(20), CreatedDateTime DATETIMEOFFSET(7) DEFAULT SYSDATETIMEOFFSET())
GO

CREATE TRIGGER [dbo].[GenerateTheName]
ON [dbo].[Table1]
AFTER INSERT
AS
BEGIN


UPDATE [dbo].[Table1]
SET [Name] = (SELECT FORMAT(GETDATE(),'yy')) + (SELECT FORMAT((ISNULL(MAX(Id), 0) + 1), '000', 'en-US') FROM Table1 WHERE CreatedDateTime > DATEADD(yy, DATEDIFF(yy, 0, (SELECT CreatedDateTime FROM inserted)), 0)
AND CreatedDateTime < (SELECT CreatedDateTime FROM inserted))
WHERE ID = (Select [id] FROM inserted)

END

insert into table1 (createddatetime) values (sysdatetimeoffset())
insert into table1 (createddatetime) values (sysdatetimeoffset())

select * from table1 -- Gives 19001 and 19002 as name

【讨论】:

  • 另外,每条插入语句都会插入三行。
  • Name 列内的 id 会增加一次,然后不会因为插入而增加。它将从 19017 到 19018 进行第一次插入,然后保持 19018 进行下一次插入。同样,对于 20001。请指导我。谢谢
【解决方案2】:

很抱歉在您询问后回复晚了,但这是我可以提供的。

据我了解,您的 id 由两部分组成:当前年份的两位数和内部唯一且始终递增的 int 数。 假设我们从 2019 年开始,第一个 ID 将是 191,然后是 192、193、.... 1920... 等等 假设 19 年的最后一个 id 是 1912345 因此,当年份更改为 2010 年时,第一个 ID 将是 2012346,然后是 2012347,依此类推。 如果上述假设是正确的,这里有一个解决方案。 你所需要的只是你已经拥有的表,我刚刚创建了一个函数,它返回你的下一个 id,你可以在触发器中、SP 中、任何你想要的地方使用它。

更新 实现另一个每年重置 id 的函数

CREATE TABLE [dbo].[Table1] (Id INT NOT NULL)
GO

CREATE FUNCTION dbo.PadLeft(@Number INT, @Zeros INT) 
RETURNS VARCHAR(100)
AS
BEGIN
DECLARE @Str VARCHAR(10) = CAST(@Number AS VARCHAR(10));
IF LEN(@Str) >= @Zeros
    RETURN @Str
RETURN RIGHT('000'+@Str, @Zeros);
END;
GO

CREATE FUNCTION GetNextId(@Year INT) 
RETURNS VARCHAR(100)
AS
BEGIN
    DECLARE @CurrentYear CHAR(2) = SUBSTRING(CAST(@Year AS CHAR(4)), 3, 2)
    DECLARE @LastIdOfCurrentYear INT;
    ;WITH data AS(
        SELECT CAST(Id AS VARCHAR(100)) AS Id FROM [dbo].[Table1] 
    )
    SELECT @LastIdOfCurrentYear = MAX(CAST(SUBSTRING(Id, 3, LEN(Id)-2) AS INT)) 
        FROM data
        WHERE SUBSTRING(Id, 1, 2) <= @CurrentYear
    DECLARE @NexId INT = ISNULL(@LastIdOfCurrentYear, 0) + 1;

    RETURN @CurrentYear + dbo.PadLeft(@NexId, 3)
END;
GO

CREATE FUNCTION GetNextIdWithReset(@Year INT) 
RETURNS VARCHAR(100)
AS
BEGIN
    DECLARE @CurrentYear CHAR(2) = SUBSTRING(CAST(@Year AS CHAR(4)), 3, 2)
    DECLARE @LastIdOfCurrentYear INT;
    ;WITH data AS(
        SELECT CAST(Id AS VARCHAR(100)) AS Id FROM [dbo].[Table1] 
    )
    SELECT @LastIdOfCurrentYear = MAX(CAST(SUBSTRING(Id, 3, LEN(Id)-2) AS INT)) 
        FROM data
        WHERE SUBSTRING(Id, 1, 2) = @CurrentYear
    DECLARE @NexId INT = ISNULL(@LastIdOfCurrentYear, 0) + 1;

    RETURN @CurrentYear + dbo.PadLeft(@NexId, 3)
END;
GO


INSERT [dbo].[Table1] (ID) VALUES (1912345)
SELECT 'Sequential' AS Info,
    dbo.GetNextId(2018) AS [If today is year 2018], 
    dbo.GetNextId(2019) AS [If today is year 2019], 
    dbo.GetNextId(2020) AS [If today is year 2020], 
    dbo.GetNextId(2021) AS [If today is year 2021]
SELECT 
    'With reset' AS Info,
    dbo.GetNextIdWithReset(2018) AS [If today is year 2018], 
    dbo.GetNextIdWithReset(2019) AS [If today is year 2019], 
    dbo.GetNextIdWithReset(2020) AS [If today is year 2020], 
    dbo.GetNextIdWithReset(2021) AS [If today is year 2021]

GO
DROP TABLE [dbo].[Table1];
DROP FUNCTION dbo.GetNextId;
DROP FUNCTION dbo.GetNextIdWithReset;
DROP FUNCTION dbo.PadLeft;

这是上面的结果:

Info        If today is year 2018   If today is year 2019   If today is year 2020   If today is year 2021
Sequential  18001                   1912346                 2012346                 2112346

Info        If today is year 2018   If today is year 2019   If today is year 2020   If today is year 2021
With Reset  18001                   1912346                 20001                   21001

【讨论】:

  • 感谢您的回复。不幸的是,你没有得到正确的要求。没有。新年的变化。假设第 19 年的 lastId 是 1912345,所以当年份更改为 2010 年时,第一个 Id 将是 20001,然后是 20002,依此类推。根据年份变化从 0 重新开始。
  • 查看更新:实现了另一个每年都会重置 ID 的功能
  • 感谢您的回复。如果删除去年的最大 id 怎么办。减去新的最大值将使新年 id 增加 1。像 20001, 20002 然后我们删除具有最大 id 的记录,例如去年的 191234,最后一个最大值将变为 191233。从新的最大值中减去将使新的 id 为 20004 而不是 20003。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多