【问题标题】:Generating the Next Id when Id is non-AutoNumber当 Id 为非自动编号时生成下一个 Id
【发布时间】:2013-07-02 07:09:01
【问题描述】:

我有一个名为 Employee 的表。 EmpId 列用作主键。在我的场景中,我不能让它自动编号。

为要插入表中的新行生成下一个 EmpId 的最佳方法是什么?

我正在使用带有 C# 的 SQL Server 2008。

这是我目前得到的代码,但要在键值对表或链接表中输入 ID(m*n 关系)

Create PROCEDURE [dbo].[mSP_GetNEXTID]
@NEXTID int out,
@TABLENAME varchar(100),
@UPDATE CHAR(1) = NULL

AS
BEGIN
    DECLARE @QUERY VARCHAR(500)

        BEGIN
        IF EXISTS (SELECT LASTID FROM LASTIDS WHERE TABLENAME = @TABLENAME and    active=1)
        BEGIN

            SELECT @NEXTID = LASTID  FROM LASTIDS WHERE TABLENAME = @TABLENAME and active=1

            IF(@UPDATE IS NULL OR @UPDATE = '')
            BEGIN
                UPDATE LASTIDS
                SET LASTID = LASTID + 1 
                WHERE TABLENAME = @TABLENAME
                and active=1
            END

        END
        ELSE
        BEGIN
            SET @NEXTID = 1

            INSERT INTO LASTIDS(LASTID,TABLENAME, ACTIVE)
            VALUES(@NEXTID+1,@TABLENAME, 1)
        END
    END

END

【问题讨论】:

标签: sql sql-server


【解决方案1】:

使用MAX(id) + 1 在性能和并发方面都是个坏主意。

相反,您应该求助于专为此类问题设计的sequences

CREATE SEQUENCE EmpIdSeq AS bigint
  START WITH 1
  INCREMENT BY 1;

并生成下一个 id 使用:

SELECT NEXT VALUE FOR EmpIdSeq;

您可以在插入语句中使用生成的值:

INSERT Emp (EmpId, X, Y)
VALUES (NEXT VALUE FOR EmpIdSeq, 'x', 'y');

甚至将其用作您的专栏的默认设置:

CREATE TABLE Emp 
(
 EmpId bigint PRIMARY KEY CLUSTERED 
    DEFAULT (NEXT VALUE FOR EmpIdSeq),
 X nvarchar(255) NULL,
 Y nvarchar(255) NULL
);

更新:以上解决方案仅适用于 SQL Server 2012+。对于旧版本,您可以使用带有标识字段的虚拟表来模拟序列行为:

CREATE TABLE EmpIdSeq (
 SeqID bigint IDENTITY PRIMARY KEY CLUSTERED
);

以及模拟NEXT VALUE的程序:

CREATE PROCEDURE GetNewSeqVal_Emp
  @NewSeqVal bigint OUTPUT
AS
BEGIN
  SET NOCOUNT ON
  INSERT EmpIdSeq DEFAULT VALUES

  SET @NewSeqVal = scope_identity()

  DELETE FROM EmpIdSeq WITH (READPAST)
END;

使用示例:

DECLARE @NewSeqVal bigint
EXEC GetNewSeqVal_Emp @NewSeqVal OUTPUT

删除最后插入的元素的性能开销将是最小的;不过,正如原作者所指出的,您可以选择删除删除语句并安排维护作业以在非工作时间删除表内容(以空间换性能)。

改编自SQL Server Customer Advisory Team Blog


Working SQL Fiddle

【讨论】:

  • SEQUENCE 是在 SQL Server 2012 中引入的,所以在这里用处不大。
  • 我已经编辑了问题并输入了我当前使用的代码,但是在键值对表或链接表(m*n 关系)中输入 ID 是问题。
  • 在这段代码中,如果我从代码中的一个位置获取下一个 Id,然后我从代码中的另一个位置获取下一个 Id,则 Id 不同步。我的意思是如果我从代码中的一个位置获取get 3 time next Id 然后从代码中的另一个地方获取这三个增量不考虑。
  • 我没有注意到数据库版本,对不起。那么,如何使用单独的表,每个表都有自己的 IDENTITY 字段来模拟序列?在SQL Server Customer Advisory Team blogthis article 查看选项 2
  • 谢谢安东尼。我不敢相信我已经打开了这个线程。我非常抱歉。当时我使用了这个策略,它对我很有效。再次抱歉。
【解决方案2】:

以上

select max(empid) + 1 from employee

是获取下一个数字的方法,但是如果有多个用户插入到数据库中,那么上下文切换可能会导致两个用户获得相同的 empid 值,然后将每个用户加 1,然后以重复 id 结束.如果您确实有多个用户,则可能必须在插入时锁定表。这不是最佳实践,这就是数据库表存在自动增量的原因。

【讨论】:

    【解决方案3】:

    我希望这对你有用。考虑到你的 ID 字段是一个整数

    INSERT INTO Table WITH (TABLOCK)
    (SELECT CASE WHEN MAX(ID) IS NULL 
      THEN 1 ELSE MAX(ID)+1 END FROM Table), VALUE_1, VALUE_2....
    

    【讨论】:

    • 我们有不止一个用户,您认为在同一时间从不同用户插入时存在任何并发问题。?
    • 查看表锁定的编辑并检查表中没有记录
    • 这会给并发带来非常糟糕的问题,尤其是随着数据的增长。
    【解决方案4】:

    试试下面的查询

    INSERT INTO Table VALUES 
    ((SELECT isnull(MAX(ID),0)+1 FROM Table), VALUE_1, VALUE_2....)
    

    你必须在最大值上检查 isnull 否则当 table 不包含 rows 时它将在最终结果中返回 null 。

    【讨论】:

    • Hiren,而 this articleJeffrey Kemp)是为 Oracle 数据库编写的,不要这样做警告和上述原因适用于 SQL Server好吧。
    • 有问题的用户明确指出他使用的是 SQL Server 2008。
    • 在使用 MAX(ID) + 1 时,与 Oracle 的 racing conditions 和性能开销相同。
    • 我认为这是解决方案之一,但它想要帮助您然后忽略它而不是投票否决。它降低了帮助任何人的道德。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-11
    • 2012-02-03
    • 2021-01-16
    • 2022-01-18
    相关资源
    最近更新 更多