【问题标题】:How to get Maximum ID value before creating a new record?如何在创建新记录之前获取最大 ID 值?
【发布时间】:2015-07-16 14:37:55
【问题描述】:

我认为在很多情况下这是一个普遍的问题,但是我在网上没有找到正确的答案。在我的 MVC 应用程序中,我有以下表格:

问题表:

---------------------------------------------
IssueID (pk) | CategoryID (fk) | IssueNumber
---------------------------------------------
   1         |     1           |      1
   2         |     1           |      2
   3         |     2           |      1
   4         |     1           |      3


分类表:

-----------------------------------
CategoryID (pk) | Prefix | Name
-----------------------------------
     1          |   COM  | Computer
     2          |   GEN  | General


在这里,我对两个类别(COM 和 GEN)都使用 IssueID(主键)字段。另一方面,我想依次生成 IssueNumber (1, 2, 3 ...),而 CategoryID 没有任何间隙。另一方面,我还想为另一个类别从 1 创建 IssueNumber,如上所示。所以问题是:

1) 上述方法有什么问题吗?如果是这样,您对此有何建议?

2) 创建记录前如何获取IssueNumber的最大值?我想在 SaveChanges() 方法上进行此操作(我使用实体框架)。由于我没有将 IssueNumber 字段设置为 PK,因此我无法让 MsSQL Server 自动增加此列的值。

任何帮助将不胜感激。提前致谢。


更新:

这是使用 lambda 的解决方案:

public int SaveIssue(Issue issue)
{
    int max = context.Issues.Where(m => m.CategoryID == issue.CategoryID ).Max(m => m.IssueNumber); 
    issue.IssueNum = max + 1; 

    context.Issues.Add(issue);
    context.SaveChanges(); 
    return issue.IssueNum ;
}

【问题讨论】:

  • 但我没有将 IssueNumber 字段设为 PK,我想为类别创建最大值 + 1。因此,在创建新 id 之前,应牢记 CategoryID 字段。你能解释一下怎么做吗?
  • @DavidW 问题不是问如何自动生成密钥,而是问如何自动生成遵循RowNumber() over (partition by CategoryID, order by IssueID) 模式的IssueNumber
  • @ScottChamberlain 绝对正确,斯科特。我误读了这个问题。谢谢。
  • 谢谢大卫,其实我想在程序方面解决这个问题。我只是想在创建新记录之前获得最大值并加 1。我知道这似乎不太好,但我不知道有什么更好的解决方案。有什么想法吗?
  • 如果您的列不可为空并且您的查询结果可以为空,请考虑我的回答How go get max value of a unique id column using LINQ

标签: sql-server asp.net-mvc entity-framework primary-key code-first


【解决方案1】:

我不确定如何在 EF 中执行此操作,但可以通过 SQL 执行以下操作(我假设 IssueID 是一个标识列)

CREATE PROCEDURE usp_CreateNewIssue
    @catagoryId int,
    @issueId int OUTPUT,
    @issueNumber int OUTPUT
AS
BEGIN
    SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
    BEGIN TRANSACTION
        declare @maxId int        
        declare @newRow table (IssueID int NOT NULL, IssueNumber int NOT NULL)

        select @maxId = MAX(IssueNumber) from Issue where CategoryID = @catagoryId

        Insert Into Issue (CategoryID, IssueNumber) values (@catagoryId, @maxId + 1)
           OUTPUT INSERTED.IssueID, INSERTED.IssueNumber 
           INTO @newRow

        select @issueId = IssueID, @issueNumber = IssueNumber from @newRow
    COMMIT TRANSACTION
END

(在浏览器中编写的代码,未经语法错误测试)

使用SET TRANSACTION ISOLATION LEVEL SERIALIZABLE 可确保在您阅读MAX(IssueNumber) 之后,在您提交事务之前,其他查询无法修改该结果。

注意:要获得良好的性能,请确保 CategoryIDIssue 上建立索引,以便 SQL 引擎可以对其执行 Key-Range lock 而不是表锁。

【讨论】:

  • 我认为 FluentAPI 可能允许一种机制通过 DBModelBuilder 对象的 MapToStoredProcedures 声明来利用这样的过程,该对象可以在 OnModelCreating 事件的覆盖中访问...
  • @Scott Chamberlain:非常感谢您的回复。为了简单起见,我用 lambda 表达式解决了这个问题,如更新的代码所示。投票+
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-23
  • 2010-11-06
  • 1970-01-01
  • 1970-01-01
  • 2013-10-23
相关资源
最近更新 更多